Invoke FunctionGraph Function from FunctionGraph¶
This page demonstrates how to call a FunctionGraph implemented in Node.js from another FunctionGraph function using API calls and temporary security credentials (SecurityAccessKey/SecurityKey/SecurityToken) provided by an agency of Agency Type Cloud Service for Cloud Service FunctionGraph Service with permission to invoke FunctionGraph. for authentication.
See: Invoking FunctionGraph Event Function using API Calls for more details on how to use the REST API.
Prerequisites¶
URN of Function to be called. In this example the code of the function to be called is:
exports.handler = async (event, context) => { const output = { 'statusCode': 200, 'headers': { 'Content-Type': 'application/json' }, 'isBase64Encoded': false, 'body': JSON.stringify(event), } return output; }
An agency of Agency Type Cloud Service for Cloud Service FunctionGraph Service with permission to invoke FunctionGraph.
The permission policy should contain following policy statement:
{ "Version": "1.1", "Statement": [ { "Action": [ "functiongraph:function:invokeAsync*", "functiongraph:function:invoke" ], "Effect": "Allow" } ] }
or use an agency with default permission FunctionGraph CommonOperations.
Note
The permissions shown above are for demonstration purpose. Please follow the principle of least privilege when creating the permission policy for the agency.
e.g. to grant permission to invoke only specific functions, the policy statement should be like:
{ "Version": "1.1", "Statement": [ { "Action": [ "functiongraph:function:invokeAsync*", "functiongraph:function:invoke" ], "Effect": "Allow", "Resource": [ "RESOURCE_PATH" ] } ] }
where “RESOURCE_PATH” is in format
FunctionGraph:::function:group/function name
By adding Function name to the end of the generated prefix, you can define a specific path.
An asterisk * is allowed to indicate any function.
For example, FunctionGraph:*:*:function:default/* indicates any function in the default group.
(Remark: changing the permission policy may take some time to take effect.)
Coding¶
package.json¶
Create a package with following content for the FunctionGraph function that will call another FunctionGraph function.
{
"name": "invoke-fg2fg",
"version": "1.0.0",
"private": true,
"description": "Invoke FunctionGraph function from FunctionGraph function",
"main": "index.js",
"type": "commonjs",
"engines": {
"node": "20.15.1"
},
"scripts": {
"postpack": "name=$(bash -c 'echo \"${npm_package_name////-}-${npm_package_version}\" | sed \"s/@/ /g\"') && rm -f ${name}.zip && tarball=\"${name}.tgz\"; tar -tf $tarball | sed 's/^package\\///' | zip -@r ${name}.zip; rm $tarball"
},
"license": "Apache-2.0",
"dependencies": {
"@opentelekomcloud-community/otc-api-sign-sdk-nodejs": "^1.0.0"
},
"bundleDependencies": [
"@opentelekomcloud-community/otc-api-sign-sdk-nodejs"
],
"files": [
"*.js",
"src/**/*",
"lib/**/*"
]
}
index.js¶
Create a function with following content to call another FunctionGraph function:
"use strict";
const https = require("https");
const {
Signer,
HttpRequest,
} = require("@opentelekomcloud-community/otc-api-sign-sdk-nodejs");
exports.handler = async function (event, context) {
// get temporary ak/sk/token from context
// to use, an agency with permission to invoke FunctionGraph is needed:
const ak = context.getSecurityAccessKey();
const sk = context.getSecuritySecretKey();
const token = context.getSecurityToken();
// get the URN of the function to be called from user data
const CALL_FG_URN = context.getUserData("CALL_FG_URN");
// get region from function URN
const region = CALL_FG_URN.split(":")[2] || "eu-de";
// FunctionGraph endpoint
const fgEndpoint = `https://functiongraph.${region}.otc.t-systems.com`;
// get projectId from Runtime environment variable (set in FG backend)
const projectId = process.env.RUNTIME_PROJECT_ID || "";
// Endpoint for asynchronous invocation
// const invokeURI = `${fgEndpoint}/v2/${projectId}/fgs/functions/${CALL_FG_URN}/invocations-async`;
// or synchronous invocation
const invokeURI = `${fgEndpoint}/v2/${projectId}/fgs/functions/${CALL_FG_URN}/invocations`;
// set body according to your function input
const body = {
key: "Hello FunctionGraph",
};
const payload = JSON.stringify(body);
// set headers
const headers = {
"Content-Type": "application/json;charset=utf8",
Host: new URL(fgEndpoint).host,
"X-Project-Id": projectId,
};
// create HttpRequest instance
const request = new HttpRequest("POST", invokeURI, headers, payload);
// create Signer instance and use temporary ak/sk/token to sign the request
const signer = new Signer();
signer.Key = ak;
signer.Secret = sk;
signer.SecurityToken = token;
// sign the request
const signedRequest = signer.Sign(request);
// send the signed request
return new Promise((resolve, reject) => {
const req = https.request(signedRequest, (res) => {
res.setEncoding("utf8");
let responseBody = "";
res.on("data", (chunk) => {
responseBody += chunk;
});
res.on("end", () => {
console.log("Response: ", responseBody);
if (res.statusCode && res.statusCode >= 400) {
reject(
new Error(
`Backend request failed with status ${res.statusCode}: ${responseBody}`,
),
);
return;
}
resolve(responseBody);
});
});
req.on("error", reject);
req.write(payload);
req.end();
});
};
Deployment¶
Create a deployment package using npm install and npm pack and deploy the package to FunctionGraph using the console as event function from scratch using Node.JS 20.15.
Configure the function:
set the handler name as index.handler.
specify an agency with permission to invoke FunctionGraph
and set the URN of the function to be called as Environment variable with key CALL_FG_URN.