@sap/faas
TypeScript icon, indicating that this package has built-in type declarations

0.7.11 • Public • Published

@sap/faas

Provides the SAP Cloud Platform Functions runtime for Node.js and basic SDK features.

Table of contents

Overview

SAP Cloud Platform Serverless Runtime provides together with other components also Functions as a service (FaaS). Developers can focus on pure application logic while writing the code, whereas FaaS takes responsibility to run the code in a secure, reliable and cost-efficient way.

Technically, FaaS runs on Kubernetes (K8s). Artifact deployments are managed using a service API, also running in K8s, next to the runtime of course. The overall architecture would allow integration of different serverless runtimes. However, currently K8s is used.

In Cloud Foundry (CF) service xsf-runtime (Extension Factory, serverless runtime) is used as entry point. Each service instance represents a K8s namespace in a shared cluster. All artifacts deployed to a given service will end up as resource in the corresponding namespace. K8s namespaces (Faas tenants) are strictly isolated from each other.

Function development is based on projects, using file faas.json as manifest. Any local IDE as well as WebIDE (Wing) can be used for implementation.

Package

This package comprises two functional parts. First, it provides the function runtime for Node.js (version 8.11.3 or higher):

  • runtime components as such
  • http server to run (or debug) functions locally
  • test runner to support function unit tests

Secondly, it offers basic SDK functionality by installing command faas-sdk:

  • check project consistency
  • create and test deployment files (values.yaml)

The function code is provided with usual js files.

'use strict';

/**
 * @param {FaasEvent} event
 * @param {FaasContext} context
 * @return {Promise|*}
 */
module.exports = function(event, context) {
    const rval = context.getSecretValueJSON('my-secret-1', 'rv.json');
    return rval.Info.Success;
};

Different trigger types are supported to invoke functions under various conditions. Secrets provide an appropriate storage for credentials. Config maps can be used in addition for any further settings.

Install

There are two FaaS client tools, waiting to support local development:

  • faas-sdk: supports the development phase, checks project consistency, runs functions locally in a similar environments like in K8s, enables unit test implementation
  • faas-cli: used to manage artifact deployment into K8s, uses FaaS instances in the Cloud Foundry as entry point

Let's start:

  • Install Cloud Foundry CLI:
  • Install xfsrt-cli:
  • Install Node.js (version 8.12.0 or higher):
  • To define devDependency to @sap/faas in your function projects
    • Run inside a project folder npm i @sap/faas
    • xfsrt-cli faas project run will also guide you here

Update the local IDE of your choice and install plugins for Node.js. Finally, enable language support for Node.js and JavaScript (ECMAScript 6): You may also find these links helpful: Javascript Reference and NodeJS Reference

The natural home of a FaaS project is a git repository. Based on that you can later easily switch between local IDE and SAP Web IDE. Hence, we recommend to set up a git repository for your project from the beginning.

Naming Rules

All runtime artifacts need a name that matches the following constraints:

  • Only lower case characters [a-z] and numbers [0-9] and - can be used.
  • Separator - must not appear at the begin or the end of the name.
  • Maximum length is restricted to 60 characters.

This applies to:

  • secrets
  • config maps
  • service references
  • functions
  • triggers
  • project names and
  • project version identifiers

Secret and config map keys (names of its entries) are usually based on file names. That's why it may contain . as well, but not at the beginning or the end.

Function Projects

We recommend using FaaS projects, as this simplifies development tasks. Each project is defined with just one file, called faas.json. Usually, it will reside next to a package.json file.

A first project can easily be created with an empty folder and the following command:

faas-sdk init -p ./my-test

It will create a simple project with one function, one secret, one http trigger and simple unit tests. After npm install all unit tests, local test run and K8s deployment will work immediately. Continue with renaming artifacts and modifying the function code.

A first example defines one single function build-qrcode. The source code is provided with file .\lib\iso-time.js. And the HTTP trigger build-qrcode will invoke the function.

{
  "project": "qrcode-producer",
  "version": "0.0.1",

  "runtime": "nodejs8",
  "library": "./lib",

  "functions": {
    "build-qrcode": {
      "module": "iso-time.js"
    }
  },

  "triggers": {
    "build-qrcode": {
      "type": "HTTP",
      "function": "build-qrcode"
    }
  }
}

A second example shows one project defining two functions, both implemented in the same module file. In this case the module file main.js exports two function handlers f1 and f2.

{
  "project": "chain",
  "version": "0.0.1",

  "runtime": "nodejs8",
  "library": "./lib",

  "functions": {
    "chain-func1": {
      "module": "main.js",
      "handler": "f1"
    },
    "chain-func2": {
      "module": "main.js",
      "handler": "f2"
    }
  },

  "triggers": {
    "chain-simple": {
      "type": "HTTP",
      "function": "chain-func1"
    }
  }
}

Project Attributes

At the top level of faas.json the following fields can be defined:

  • project: used as label for all runtime artifacts (see restrictions to naming rules)
  • version: used as label for all runtime artifacts (see restrictions to naming rules)

Specific to the runtime:

  • runtime: used to select the runtime, currently only nodejs8 is supported
  • library: base directory for all source code file references, just to reduce redundancy

Runtime artifacts:

  • secrets: collection of secrets, used to store credentials, api keys and the like
  • configs: collection of config maps, used for less critical, configuration-like settings, that shall not be hard-coded
  • functions: collection of functions that this project defines
  • triggers: collection of triggers, different types with individual settings

Each runtime artifact collection uses the object name as json key field. The name must be unique per artifact type in the scope of one K8s namespace (that is, service instance).

For example:

  • Multiple namespaces may define their own secret test1.
  • In one namespace there might be a function test1 and a secret test1 in parallel.
  • However, there will be only one secret test1 in one namespace at a time, even if two different projects are used to deploy it.

No assumptions are made regarding the separation of projects. In particular, FaaS runtime will not prevent multiple projects from deploying any artifacts concurrently.

Secrets and Config Maps

Technically, secrets and config maps share the same structure. A local directory containing one or more files is used for declaration. All files together must not exceed a total size of 1Mb.

{
  "project": "example",
  "version": "0.0.1",

  "runtime": "nodejs8",
  "library": "./lib",

  "secrets": {
    "sec1": {
      "source" : "./data/sec1"
    }
  },

  "configs": {
    "cfg1": {
      "source" : "./data/cfg1"
    }
  }
}

In addition to the unique object name, for example, sec1 or cfg1, only one attribute is required:

  • source: relative path to the data directory

The data directory and its contained files define the data model and default values. As the files will most likely show up in a git repository you may wish to see placeholder values first, later replaced (dynamically) by valid credentials, for instance.

Both tools, faas-sdk and faas-cli, support a simple mechanism for that: a deployment file with a predefined structure. Take this as an example:

secret-values:
  sec1:
    rv.json:
      Info:
        Success: Demo
        Failure: Todo
      Code:
        Success: A
        Failure: X
    text: Nice Test!

Here, values are defined for secret sec1, and its two files rv.json and text. For json or yaml files also single object attributes inside the file can be replaced. Arrays are handled as one value only. In the case of other files e.g. text or binary data, the whole file content can be replaced.

To initialize such a deployment file based on specific secret definitions run inside the project:

faas-sdk init-values -y values.yaml

The command will search faas.json starting from the current working directory. Once found it will add the content of secret and config map files to the deployment file values.yaml. As no directory is specified it will be stored in folder deploy (to be ignored by git) next to faas.json.

After adjusting the deployment file content you can start a local test run:

faas-sdk run -y values.yaml

And for cloud deployment it can be used as well:

faas-cli project deploy -y ./deploy/values.yaml -s my-cf-service -k my-cf-service-key

These deployment files can also be used to define mock data for function unit tests.

Service References

Besides the option to use secrets there is a more simple way to provide credentials of SAP CP platform services to functions or triggers. First, you can use the command line tool to transfer service keys to a secure store in the faas runtime:

  • Run cf login
  • Run faas-cli login
  • Run faas-cli service register -s <service-name> -b <service-key>

To list all registered services (for your faas tenant, means service instance):

  • Run faas-cli service list

Then you can define in your project a service reference, for example to an enterprise messaging instance:

{
  "services": {
    "my-ems": {
      "type": "enterprise-messaging",
      "instance": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "key": "xxx"
    }
  }
}

Finally, use the alias my-ems inside faas.json at trigger definitions and/or within function code. The specific values of the reference can be provided with deploy values as well.

Functions

Let's start with an example again:

{
  "project": "example",
  "version": "0.0.1",

  "runtime": "nodejs8",
  "library": "./lib",

  "functions": {
    "my-new-fnc": {
      "module" : "index.js",
      "handler": "",
      "secrets": [ "sec-xbem", "sec-ebaas" ],
      "configs": [],
      "timeout": 180,
      "maxBody": "1MiB"
    }
  }
}

In addition to the unique object name my-new-fnc the following attributes can be provided:

  • module: source code file that exports the function handler (it may require other files of course)
  • handler: only needed if module.exports appears as an object where handler can be found as attribute
  • httpApi: indicates whether or not HTTP request and response are required by the function handler
  • secrets: array of secrets used by the function
  • configs: array of config maps used by the function
  • timeout: seconds the function can run, minimum between 10 and 180 seconds
  • maxBody: limit for body size (payload) of incoming requests provided to the function, default 1MiB

The function handler may require built-in modules, further local module files or external packages. External dependencies (or devDependencies, for example for unit testing) are defined as usual in package.json.

An important entry in devDependencies is @sap/faas. It allows the implementation of function unit tests. Furthermore, it offers type definitions that your local IDE may use for code proposals.

{
  "dependencies": {
  },
  "devDependencies": {
    "@sap/faas": ">=0.7.6"
  }
}

The function handler in file ìndex.js may look like this:

'use strict';

/**
 * @namespace Faas
 * @typedef {import("@sap/faas").Faas.Event} Faas.Event
 * @typedef {import("@sap/faas").Faas.Context} Faas.Context
 */

/**
 * @param {Faas.Event} event
 * @param {Faas.Context} context
 * @return {Promise<*>|*}
 */
module.exports = function(event, context) {
    const rval = context.getSecretValueJSON('sec1', 'rv.json');
    return rval.Info.Success;
};

Please note how jsdoc annotations are used to declare the types. With that IDE shall support you in finding methods and attributes of event and context while typing.

Function Triggers

Functions are invoked by triggers. A single function may be referenced by multiple trigger instances of different types. In principle a single trigger may also invoke different functions, e.g. an AMQP trigger with multiple rules.

The following types are supported:

  • HTTP
  • Timer
  • AMQP
  • CloudEvents

A first example shows how triggers are defined in principle within faas.json:

{
  "project": "example",
  "version": "0.0.1",

  "runtime": "nodejs8",
  "library": "./lib",

  "functions": {
    "my-new-fnc": {
      "module" : "index.js"
    }
  },

  "triggers": {
    "demo": {
      "type": "HTTP",
      "function": "my-fnc-01"
    },
    "job1": {
      "type": "Timer",
      "schedule": "0/15 * * * *",
      "function": "my-fnc-02"
    }
  }

}

Trigger demo will provide an HTTP endpoint. For each received HTTP request the function my-fnc-01 will be invoked. In parallel trigger job1 will call function my-fnc-02 each quarter of an hour.

HTTP Trigger

For each trigger instance an external HTTP endpoint will be created. Each incoming request will be forwarded to the function, except those for method 'OPTIONS'. The result of the function will be returned as response.

Attributes:

  • function: the function to call

Example:

{
  
  "triggers": {
    "demo": {
      "type": "HTTP",
      "function": "my-new-fnc"
    }
  }
  
}

Be aware that the endpoint is public visible. So far authentication/authorization has to be handled by the function code. It is planned to support automated oauth token validation in future.

Timer Trigger

A single Timer trigger defines a schedule, optionally with timezone to call a selected function. Different schedule formats are supported:

  • simple duration

    • every 1 minute and 30 seconds: 1m30s,
    • every 15 seconds 15s,
    • every 1 hour 30 minutes and 15 seconds: 1h30m15s
  • cron expression, spec conform, 5 fields

    ┌───────────── minute (0 - 59)
    │ ┌───────────── hour (0 - 23)
    │ │ ┌───────────── day of the month (1 - 31)
    │ │ │ ┌───────────── month (1 - 12)
    │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday; 7 is also Sunday on some systems)
    │ │ │ │ │
    
    * * * * *
    
  • cron expression, additional field for seconds, 6 fields

    ┌───────────── second (0 - 59)
    │ ┌───────────── minute (0 - 59)
    │ │ ┌───────────── hour (0 - 23)
    │ │ │ ┌───────────── day of the month (1 - 31)
    │ │ │ │ ┌───────────── month (1 - 12)
    │ │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday; 7 is also Sunday on some systems)
    │ │ │ │ │ │
    
    * * * * * *
    

Together with cron expressions also a timezone can be of importance, typically when scheduling for hours. Valid timezone values follow Iana timezone database.

Attributes:

  • schedule: duration or cron expression, which defines the execution interval
  • timezone: optional, timezone
  • function: the function to call

Examples for schedule and timezone:

schedule timezone execution
30s every 30 seconds, start immediately, 10:15:12, 10:15:42, 10:16:12 ...
1h30m every one and a half hour, 10:12:10, 11:42:10, 13:12:10, 14:42:10 ...
0 * * * * each full hour
0/15 * * * * each quarter of an hour
0/15 * * * * * each 15 seconds, 10:00:00, 10:00:15, 10:00:30, 10:00:45, 10:01:00 ...
30 4 * * * Asia/Tokyo each day 04:30, Tokyo time
15 8 * * * Europe/Berlin each day 08:15, Berlin time
20-24/2 * * * * each hour the 20th, 22nd and 24th minute

The minimum interval of a timer is 1 second, runtime will either round up or raise an error if syntax is wrong. If a function call takes longer than the scheduled interval, the timer will skip those calls and log this accordingly. If a function can not be reached, the timer will repeat to try this a defined number of times with a growing delay.

Updates to timer definitions may require a timer restart, which may be of importance when scheduling hours or longer intervals. For example, if you define a timer schedule of 1h and change the timer 5 minutes before execution to 1h30m you will have to wait full 1h30m for the next call.

A timer is restarted if schedule or timezone change, otherwise it is only updated, keeping the calculated point in time for the next execution.

AMQP Trigger

This trigger type invokes functions as message consumers. As long business events are transported via messages the trigger can also be seen as a business event trigger.

Depending on rules functions are selected based on message properties. The function results can optionally be sent as new messages. If the function execution fails the corresponding message can also be forwarded to an error topic.

Technically the trigger behaves like an AMQP 1.0 client. It will connect to any peer either via WebSocket (with or without OAuth credentials flow) or via plain TCP, most likely using SASL credentials. Usually, the peer will be a message broker like Enterprise Messaging provides it for example.

Attributes:

  • service: a service alias, it must be defined in the same project
  • secret: a secret that provides credentials to connect if no service reference is provided
  • config: a config map that provides the amqp link settings and binding rules

Usually, the secret can be re-used by multiple triggers as it provides just connection data for a messaging broker. The config map will be trigger specific, providing link settings (to address queues or topics) as well as binding rules.

Secret Entries:

  • dial or dial.json: destination data like host, port, use of websocket or not, oauth credentials if needed
  • sasl or sasl.json: SASL mechanism (ANONYMOUS, PLAIN), corresponding parameters if plain TCP connections are used

Config Entries:

  • amqp or amqp.json: definition of AMQP links and sessions to use, addresses, settle modes and so on
  • bind or bind.json: rules to select functions based on message properties and to specify result and error handling

It will be important to use a deployment file (values.yaml) to provide real credentials and settings for a specific deployment. Those data must not be stored in the secret and config defintion fields directly, as you probably will not wish to see it on git.

CloudEvents Trigger

This trigger allows you to subscribe CloudEvents and to define a rule-based function invocation. It works similar to an AMQP trigger, but does not require secret or config map. Instead it is completely defined within faas.json. Furthermore, it will configure the messaging service automatically with the project deployment.

{
  "functions": {
    "ce-coffee-handler": {
      "module": "index.js",
      "services": []
    }
  },
  "triggers": {
    "my-ce": {
      "type": "CloudEvents",
      "service": "my-ems",
      "rules": [
        {
          "ce-source": "",
          "ce-type": "com.sap.coffee.required",
          "function": "ce-coffee-handler",
          "failure": "accept"
        },
        {
          "ce-source": "",
          "ce-type": "com.sap.coffee.produced",
          "function": "ce-coffee-handler",
          "failure": "accept"
        },
        {
          "ce-source": "",
          "ce-type": "com.sap.coffee.consumed",
          "function": "ce-coffee-handler",
          "failure": "accept"
        }
      ]
    }
  }
}

While deployment the referenced messaging service will be called to create a queue and topic subscriptions according to the given rules. For one single message only one single rule is applied, the first that matches. Currently SAP Enterprise Messaging is supported. The list may be extended according to development requests you raise.

Function Runtime API

The function handler is implemented as usual (anonymous) function, receiving two parameters: event and context. It may return a simple value directly or a Promise to handle asynchronous execution. As a consequence, async and await may be used as well.

Global Variables

Multiple calls may arrive in parallel in one Node.js instance. Each call will be handled in a sandbox. This means global variables of the main file are never shared and never reused.

However, the sandbox does not cover globals of any dependent file. Hence, global variables in required modules must be handled with care if it cannot be avoided. Valid examples for those globals could be:

  • A cache for access tokens, not retrieved for each single call
  • Protocol clients that manage a permanent network connection, e.g. to a message broker

Still, there is no guarantee that a Node.js instance is running for a certain time, but in fact such a cache or reused client will be reused many times and will improve overall performance significantly.

Handler Exceptions

The function code can throw an exception at any point in time to stop processing unexpected internal state. In this case an HTTP caller for example will receive response status code 500 (internal server error) only. Error details will be added to the function log. faas-cli can be used to retrieve log entries.

Handler Parameter: event

Provides attributes and methods related to a single function invocation.

Attributes:

  • auth: authorization data
    • type: first part of HTTP authorization header, e.g. Basic, Bearer, ..:
    • credentials: plain data corresponding to type
  • ce: cloud event context attributes, only defined if available
    • specVersion: version of the CloudEvents specification which the event uses
    • source: event producer "sap/app/01"
    • type: event type, e.g. "sap.common.alert"
    • id: ID of the event
    • subject: subject of the event
    • time: timestamp of when the event happened
    • dataContentType: data encoding format
    • dataSchema: link to the schema that the data attribute adheres to
    • data: event payload, please note event.ce.data will always be equal to event.data
    • extensions: additional metadata not covered by the specification
  • data: payload data (HTTP body) related to invocation, depending on the received content type, either string, Buffer or Object, data can always be found here, with or without cloud event.
  • http: only defined if explicitly requested, provides access to HTTP request and response if available

Methods:

  • decodeJsonWebToken():{ header: object, payload: object, signature: string }: Decodes event credentials as JSON WebToken (JWT). Returns null if the mechanism is not 'Bearer' or if the token has no valid JWT structure. Does not validate the token signature, as this has been done by the calling trigger already. The token itself is provided in field event.auth.credentials.
  • decodeUserPassword():{ user: string, password: string }: Decodes event credentials as basic authentication data. Returns null if the mechanism is not 'Basic' or if the credentials do not match the expected structure. Does not validate the credentials against any provider, as this will be function logic already.
  • setBadRequest(): Sets the event status to bad request. Event will not be processed, handler may still add data to the response, for example as hint. In contrast, throwing any error would be treated as internal error.
  • setUnauthorized(): Sets the event status to unauthorized. Event will not be processed, handler may still add data to the response, for example as hint. In contrast, throwing any error would be treated as internal error.
  • getContentType():string: Provides the received content type. In the case of cloud events it will be taken from the event itself. Data are provided accordingly, this means as object in case of json-format or as string or Buffer otherwise.
  • setResponseType(string): Defines the response content type explicitly. Otherwise callers' accepted types will be compared with the return data type and best matches will be used. Fallback strategy is based on the returned data type only.
  • getResponseStream(contentType):WritableStream: The response content type is defined and the corresponding stream is returned. It is possible to write data directly to the stream or to pipe data from other streams. The function handler shall return a Promise to the runtime to indicate the asynchronous end of processing.
  • sendResponseEvent(ce): The cloud event is returned as result of function execution. Http-Triggers would return it to the caller. AMQP or CE trigger can send the event, depending on its configuration. Source will be adjusted.

If the function returns a simple value without defining the content type, a matching response content type will be selected automatically. And if the client was sending an HTTP request with Accept header this will also be taken into account.

Handler Parameter: context

Provides attributes and methods related to the current process.

Attributes:

  • funcName: function name
  • timeoutMS: milliseconds that the function is allowed to run

Methods: (runtime nodejs8 provides the same methods, but not async)

  • async getServiceCredentials(faas-json-alias): provides service credentials as binary data (Buffer)`
  • async getServiceCredentialsString(faas-json-alias): provides service credentials as text`
  • async getServiceCredentialsJSON(faas-json-alias): provides service credentials as parsed JSON data`
  • async getSecretValueStream(name, key): provides secret value stream
  • async getSecretValue(name, key): provides secret value as binary data (Buffer)
  • async getSecretValueString(name, key): provides secret value as text
  • async getSecretValueJSON(name, key): provides secret value as parsed JSON data
  • async getSecretValueYAML(name, key): provides secret value as parsed YAML data
  • async getConfigValueStream(name, key): provides config value stream
  • async getConfigValue(name, key): provides config value as binary data (Buffer)
  • async getConfigValueString(name, key): provides config value as text
  • async getConfigValueJSON(name, key): provides config value as parsed JSON data
  • async getConfigValueYAML(name, key): provides config value as parsed YAML data
  • async callFunction(name, content): response: Calls another function by name within the same K8s namespace, content.data provides the request payload, content.type the request content type. response.data contains the received payload, response.type the received content type. Special handling with runtime nodejs8: the method accepts a JsCallback as third parameter or returns a promise otherwise.
  • getFunctionEndpoint(name):string: Only available with enabled HTTP API, expects a function name (inside the same K8s namespace) and returns the corresponding HTTP endpoint.

The object is reused for all function calls within the current Node.js instance, but attributes must not be changed.

Function Test

The following command runs all functions of a project locally:

faas-sdk run -r 7777 -y values.yaml

It will search faas.json starting from the current working directory. After reading all declarations one HTTP server starts listening at the given port. The URL for each function is printed to the console.

Each function will find the same environment like in the cloud later on. Use any HTTP client or just a Web browser to call it.

Function Debugging

To debug function code in a local IDE, usually a debug configuration is needed:

  • Install @sap/faas as devDependency.
  • Create a debug configuration in your local IDE for Node.js.
  • Use ./node_modules/@sap/faas/lib/cli.js as file to execute.
  • Use run as cli argument.
  • Use this directory or just another underneath faas.json as working directory.
  • Set break points and invoke a function via browser or HTTP client.

Function Unit Tests

Finally, also unit tests for functions can be implemented. Here, @sap/faas provides a test method. It will do the following:

  • Start an HTTP server as described before.
  • Execute the provided test callback.
  • Shut down the server afterwards.

As entry point the same context object is provided that functions see at runtime. It allows to call functions and to read secret or config map values, at this point of course starting from the local files. A deployment file can be provided to apply mock data.

The following example uses a well-known test framework, even if @sap/faas does not enforce its usage.

/*jshint mocha:true*/
'use strict';

const assert = require('assert');
const faas = require('@sap/faas');

describe('hello secret example', () => {

//  ************************************************************************************************

    it('using default values', (done) => {
        faas.test(done,
            {
            },
            async (context) => {
                const result = await context.callFunction('hello-secret', {});
                assert.equal(result.type, 'text/plain; charset=utf-8');
                assert.equal(result.data, 'Demo');
            }
        );
    });

//  ************************************************************************************************

    it('using deploy values', (done) => {
        faas.test(done,
            {
                'deploy-values': '../mock/values.yaml'
            },
            async (context) => {
                const result = await context.callFunction('hello-secret', {});
                assert.equal(result.type, 'text/plain; charset=utf-8');
                assert.equal(result.data, 'Nice Test!');
            }
        );
    });

//  ************************************************************************************************

    it('read secret text', (done) => {
        faas.test(done,
            {
            },
            async (context) => {
                assert.equal(context.getSecretValueString('sec1', 'text'), 'Hello World!');
            }
        );
    });

//  ************************************************************************************************

    it('read secret json', (done) => {
        faas.test(done,
            {
            },
            async (context) => {
                assert.deepStrictEqual(context.getSecretValueJSON('sec1', 'rv.json'), {
                    "Info": {
                        "Success": "Demo",
                        "Failure": "Todo"
                    },
                    "Code": {
                        "Success": "A",
                        "Failure": "X"
                    }
                });
            }
        );
    });

//  ************************************************************************************************

});

Versions

Current Tags

  • Version
    Downloads (Last 7 Days)
    • Tag
  • 0.7.11
    0
    • latest

Version History

Package Sidebar

Install

npm i @sap/faas

Weekly Downloads

0

Version

0.7.11

License

none

Unpacked Size

180 kB

Total Files

36

Last publish

Collaborators

  • sap_extncrepos