InCountry Serverless Documentation
About
Serverless is a part of the InCountry Platform. It lets you execute serverless scripts on protected data so that it does not leave the originating country. All the calculations, aggregation, and validations are performed within this country and do not reach your application server, located in another country. This way, you do not violate the local regulations and keep compliance with all data protection and localization laws of the data originating country.
Get started with Serverless
The Serverless component of the InCountry Platform allows you to execute serverless scripts on regulated data in the originating country.
Management of serverless scripts is available on the InCountry Portal. If you need to automate the management and execution of serverless scripts, you can use REST API.
To get started with Serverless, please follow these steps:
Create an environment for the country where you want to execute serverless scripts.
Create an integration of the Serverless type.
Use the certificate and private key to manage serverless scripts with REST API.
Execute serverless scripts remotely.
Authorization of requests to REST API
Authentication and authorization of requests to REST API are performed with a certificate and a private key.
It would be best if you created a new integration of the Serverless type on the InCountry Portal. Upon creation, InCountry Portal will prompt you to download a certificate and a private key restricted to a specific REST API endpoint in some countries.
Once you get your certificate and private key, you can use them to authorize data requests to REST API.
Within each request, you need to specify the paths to the certificate and the private key. Without doing this, all your requests will be unauthorized and rejected by REST API.
In examples of cURL requests, you can see the following request parameters:
--cert ./{certificateName}.pem \
--key ./{keyName}.key \
Within these parameters you need to replace the {certificateName}
and {keyName}
strings with their actual names.
While performing requests to a specific country, please ensure that you have specified the correct country code within the request and you are using the certificate and private key which attribute to this country.
Management of serverless scripts
Please check documentation for REST API before proceeding to this section.
Management of serverless scripts allows you to create additional record validation and data calculation mechanisms for protected data stored in countries that impose enforced data regulation.
You can manage serverless scripts within the InCountry Platform as follows:
publish serverless scripts
execute serverless scripts
get the list of serverless scripts
get a specific serverless script
delete the no longer needed serverless scripts
Publishing a serverless script
POST /serverless/publish
- Request parameters
Parameters | Type | Description |
---|---|---|
scriptName | string | Name of a serverless script that is published. |
scriptBody | string | Body of the serverless script. |
options | object | A JSON object with the country and forceUpdate parameters. |
country | string | A country to which a server script is published to. |
forceUpdate | boolean | A flag that the serverless script must be forcedly updated. |
- cURL request
curl --request POST \
--url https://{restApiURLAddress}/serverless/publish \
--header 'Content-Type: application/json' \
--cert ./{certificateName}.pem \
--key ./{keyName}.key \
--data '{
"scriptName": "{SCRIPT-NAME}",
"scriptBody": "module.exports.handler = async (storage, country, params, modules) => { const recordData = { recordKey: '\''UniqRecordKey'\'', body: params.bodyParam, }; console.log(recordData); const writeResponse = await storage.write(country, recordData); return { result: '\''ok'\'' }; };",
"options": {
"country": "se",
"forceUpdate": false
}
}'
- Responses
STATUS 201 - plain
This response is returned when the serverless script has been successfully published.
{"scriptName":"{SCRIPT-NAME}","scriptBody":"module.exports.handler = async (storage, country, params, modules) => { const recordData = { recordKey: 'UniqRecordKey', body: params.bodyParam, }; console.log(recordData); const writeResponse = await storage.write(country, recordData); console.log(writeResponse); return { result: 'ok' }; };"}
STATUS 401 - This response is returned when the request is unauthorized.
STATUS 409 - This response is returned when the serverless script with the current name already exists.
STATUS 5** - Server error.
Executing a serverless script
The endpoint executes the published serverless script synchronously and returns its output and meta information.
POST /serverless/execute
- Request parameters
Parameters | Type | Description |
---|---|---|
scriptName | string | Name of a serverless script that is executed. |
options | object | Object with the country parameter. |
country | string | A country in which a server script is executed against. |
- cURL request
curl --request POST \
--url https://{restApiURLAddress}/serverless/execute \
--header 'Content-Type: application/json' \
--cert ./{certificateName}.pem \
--key ./{keyName}.key \
--data '{
"scriptName": "{SCRIPT-NAME}",
"options": {
"country": "se"
},
"scriptParams": {
"bodyParam": "test"
}
}'
- Responses
STATUS 201 - plain
This response is returned when the serverless script has been successfully executed.
{"result":{"result":"ok"},"duration":1381,"error":null}
STATUS 400 - This response is returned when the request is incorrect.
{"result":null,"duration":775,"error":"InputValidationError: delete() Validation Error: <RecordKey> should be RecordKey but got {\"recordKey\":\"UniqRecordKey\",\"body\":\"test\"}: Record key must be a non-empty string"}
STATUS 401 - This response is returned when the request is unauthorized.
STATUS 404 - This response is returned when the serverless script has not been found.
STATUS 5** - Server error.
Getting a list of serverless scripts
The endpoint returns a list of published serverless scripts. It supports pagination and does not return bodies of serverless scripts.
GET /serverless/scripts
- Request parameters
Parameters | Type | Description |
---|---|---|
offset | object | Some items to skip before returning a list of serverless scripts. |
limit | string | The maximal number of serverless scripts to return. The maximal number is limited to 100. |
- cURL request
curl --request GET \
--url "https://{restApiURLAddress}/serverless/functions?limit=10&offset=0" \
--cert ./{certificateName}.pem \
--key ./{keyName}.key
- Responses
STATUS 201 - plain
This response returns a list of serverless scripts.
{"data":[{"script_name":"{SCRIPT-NAME}","created_at":"2021-03-30T12:50:45.000Z","updated_at":"2021-03-30T12:59:47.000Z"},{"script_name":"e2e-script-1-53203","created_at":"2021-03-26T10:13:30.000Z","updated_at":"2021-03-26T10:13:30.000Z"},{"script_name":"e2e-script-66046","created_at":"2021-03-26T10:13:29.000Z","updated_at":"2021-03-26T10:13:29.000Z"},{"script_name":"e2e-script-1-30449","created_at":"2021-03-26T09:02:30.000Z","updated_at":"2021-03-26T09:02:30.000Z"},{"script_name":"e2e-script-26244","created_at":"2021-03-26T09:02:29.000Z","updated_at":"2021-03-26T09:02:29.000Z"}],"meta":{"count":5,"limit":10,"offset":0,"total":5}}
STATUS 400 - This response is returned when specified parameters are incorrect.
STATUS 401 - This response is returned when the request is unauthorized.
STATUS 5** - Server error.
Getting a serverless script
The endpoint returns information about a specific serverless script.
GET /serverless/scripts/{scriptName}
- Request parameters
Parameters | Type | Description |
---|---|---|
scriptName | string | Name of a serverless script which information should be returned. |
- cURL request
curl --request GET \
--url https://{restApiURLAddress}/serverless/functions/{SCRIPT-NAME} \
--cert ./{certificateName}.pem \
--key ./{keyName}.key
- Responses
STATUS 201 - plain
This response returns a serverless script with its information.
{"script_name":"{SCRIPT-NAME}","script_body":"module.exports.handler = async (storage, country, params, modules) => { const recordData = { recordKey: 'UniqRecordKey', body: params.bodyParam, }; console.log(recordData); const writeResponse = await storage.write(country, recordData); return { result: 'ok' }; };","created_at":"2021-03-30T12:50:45.000Z","updated_at":"2021-03-30T12:59:47.000Z"}%
STATUS 401 - This response is returned when the request is unauthorized.
STATUS 404 - This response is returned when the specified serverless script has not been found.
STATUS 5** - Server error.
Deleting a serverless script
The endpoint deletes a specifiс serverless script.
DELETE /serverless/scripts/{scriptName}
- Request parameters
Parameters | Type | Description |
---|---|---|
scriptName | string | Name of a serverless script that should be deleted. |
- cURL request
curl --request DELETE \
--url https://{restApiURLAddress}/serverless/functions/{SCRIPT-NAME} \
--cert ./{certificateName}.pem \
--key ./{keyName}.key
- Responses
STATUS 204 - plain
This response is returned when the serverless script has been successfully deleted.
STATUS 401 - This response is returned when the request is unauthorized.
STATUS 404 - This response is returned when the specified serverless script has not been found.
STATUS 5** - Server error.
Serverless script examples
All serverless scripts should be written as a JavaScript function.
Below you can find an example of the serverless script.
In the current examples, storage
is an instance of the Storage
class. Please check our Node.js SDK documentation on how to use it properly.
Example #1 - Checking the existence of the record with a specific email
module.exports.handler = async (storage, country, params, modules) => {
const result = await storage.find(country, { key1: params.email });
if (result.records.length > 0) {
return true;
}
return false;
};
Example #2 - Showing how to use all the methods in a junction
module.exports.handler = async (storage, country, params, modules) => {
let result;
const validateRecord = (expectedRecord, actualRecord, methodValue) => { // method to compare actual record with expected
if (expectedRecord.recordKey !== actualRecord.recordKey) {
return `bad recordKey ${methodValue}`;
}
if (expectedRecord.body !== actualRecord.body) {
return `bad body ${methodValue}`;
}
if (expectedRecord.key1 !== actualRecord.key1) {
return `bad key1 ${methodValue}`;
}
if (expectedRecord.key2 !== actualRecord.key2) {
return `bad key2 ${methodValue}`;
}
if (expectedRecord.key3 !== actualRecord.key3) {
return `bad key3 ${methodValue}`;
}
if (expectedRecord.key10 !== actualRecord.key10) {
return `bad key10 ${methodValue}`;
}
if (expectedRecord.profileKey !== actualRecord.profileKey) {
return `bad profileKey ${methodValue}`;
}
if (expectedRecord.serviceKey1 !== actualRecord.serviceKey1) {
return `bad serviceKey1 ${methodValue}`;
}
if (expectedRecord.rangeKey1 !== actualRecord.rangeKey1) {
return `bad rangeKey1 ${methodValue}`;
}
if (expectedRecord.rangeKey10 !== actualRecord.rangeKey10) {
return `bad rangeKey10 ${methodValue}`;
}
return 'ok';
};
const validateMeta = (expectedCount, expectedTotal, expectedOffset, expectedLimit, actualMeta, methodValue) => { // method to compare actual metadata with expected
if (expectedCount !== actualMeta.count) {
return `bad count ${methodValue}`;
}
if (expectedTotal !== actualMeta.total) {
return `bad total ${methodValue}`;
}
if (expectedOffset !== actualMeta.offset) {
return `bad offset ${methodValue}`;
}
if (expectedLimit !== actualMeta.limit) {
return `bad limit ${methodValue}`;
}
return 'ok';
};
const recordData = {
recordKey: params.recordKeyParam,
body: params.bodyParam,
key1: params.key1Param,
key2: params.key2Param,
key3: params.key3Param,
key10: params.key10Param,
profileKey: params.profileKeyParam,
serviceKey1: params.serviceKey1Param,
rangeKey1: params.rangeKey1Param,
rangeKey10: params.rangeKey10Param,
};
const writeResult = await storage.write(country, recordData);
const readRecord = await storage.read(country, params.recordKeyParam);
result = validateRecord(recordData, readRecord.record, 'read'); // compares if the written record matches the read one
if (result !== 'ok') {
return result;
}
const findOneRecord = await storage.findOne(country, { recordKey: params.recordKeyParam });
result = validateRecord(recordData, findOneRecord.record, 'findOne by recordKey'); // compares if the written record matches the found one by the recordKey with the findOne method
if (result !== 'ok') {
return result;
}
const findOneRecord2 = await storage.findOne(country, { key2: params.key2Param });
result = validateRecord(recordData, findOneRecord2.record, 'findOne by key2'); // compares if the written record matches the found one by the key2 with the findOne method
if (result !== 'ok') {
return result;
}
const limit = 10;
const offset = 0;
const findResponse = await storage.find(country, { recordKey: params.recordKeyParam }, { limit, offset });
const actualMeta = findResponse.meta;
result = validateMeta(1, 1, offset, limit, actualMeta, 'find by recordKey'); // compares if the written record meta matches the found record by the recordKey meta
if (result !== 'ok') {
return result;
}
const findRecord = findResponse.records[0];
result = validateRecord(recordData, findRecord, 'find by recordKey'); // compares if the written record matches the found one by the recordKey with the find method
if (result !== 'ok') {
return result;
}
const findResponse2 = await storage.find(country, { rangeKey10: params.rangeKey10Param }, { limit, offset });
const actualMeta2 = findResponse2.meta;
result = validateMeta(1, 1, offset, limit, actualMeta2, 'find by rangeKey10'); // compares if the written record meta matches the found record by the rangeKey10 meta
if (result !== 'ok') {
return result;
}
const findRecord2 = findResponse2.records[0];
result = validateRecord(recordData, findRecord2, 'find by rangeKey10'); // compares if the written record matches the found one by the rangeKey10 with the find method
if (result !== 'ok') {
return result;
}
await storage.delete(country, params.recordKeyParam); // delete record
return result;
};