ServisBOT BaaS is the home for mappings between the ServisBOT platform and external services such as APIs and SDKs. We currently have two kinds:
BaaS API connectors work best for RESTfull APIs, but with the full power of AWS available in the platform, providing custom integrations other services should still be possible.
We have created a mock API and examples for creating your first series of API connectors.
Get Started
The api-connector is used to hit HTTP endpoints. For example:
{
"Alias": "phoneNumberValidate",
"Body": {},
"Endpoint": "https://password-reset.herokuapp.com/validatePhoneNumber",
"Headers": {
"Authorization": "************************",
"Content-Type": "application/json"
},
"Method": "POST",
"RequestMapping": {
"telephoneNumber": {
"inputPath": "$.telephoneNumber",
"requestBodyPath": "$.telephoneNumber",
"type": "requestBody"
}
},
"ResponseMapping": {
"telephoneNumber": {
"outputPath": "$.telephoneNumber",
"responseBodyPath": "$.telephoneNumber",
"type": "responseBody"
}
}
}
This BAAS will hit the endpoint called out in the Endpoint parameter with a POST from the Method parameter. The Authorization
and Content-Type
headers will be added to the request and the RequestMapping
and ResponseMapping
will work per the next section of this document.
When using the application/x-www-form-urlencoded Content-Type
in your Headers
, you can specify all of your parameters in the Endpoint
using the requestURL
type in your RequestMapping
{
"Method": "POST",
"Endpoint": "https://httpbin.org/post?keyone=${keyone}&keytwo=${keytwo}",
"Headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"Alias": "urlencodedUsingRequestUrl",
"RequestMapping": {
"keyoneEntry": {
"inputPath": "$.keyone",
"requestParameter": "keyone",
"type": "requestURL"
},
"keytwoEntry": {
"inputPath": "$.keytwo",
"requestParameter": "keytwo",
"type": "requestURL"
}
},
"ResponseMapping": {
},
"Type": "api-connector",
"Body": {}
}
Alternatively if you have a lot of request parameters and don’t want to build up a large Endpoint
manually, you can specify a base Endpoint
, and pass in key, value pairs to the body using the requestBody
type in your RequestMapping
.
{
"Method": "POST",
"Endpoint": "https://httpbin.org/post",
"Headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"Alias": "urlencodedUsingRequestBody",
"RequestMapping": {
"keyoneEntry": {
"inputPath": "$.keyone",
"requestBodyPath": "$.keyone",
"type": "requestBody"
},
"keytwoEntry": {
"inputPath": "$.keytwo",
"requestBodyPath": "$.keytwo",
"type": "requestBody"
}
},
"ResponseMapping": {
},
"Type": "api-connector",
"Body": {}
}
The private-api-connector is used to hit HTTP endpoints, via VPC and NAT so it always comes from a known host ip address. For example:
{
"Alias": "phoneNumberValidate",
"Type": "private-api-connector",
"Body": {},
"Endpoint": "https://password-reset.herokuapp.com/validatePhoneNumber",
"Headers": {
"Authorization": "************************",
"Content-Type": "application/json"
},
"Method": "POST",
"RequestMapping": {
"telephoneNumber": {
"inputPath": "$.telephoneNumber",
"requestBodyPath": "$.telephoneNumber",
"type": "requestBody"
}
},
"ResponseMapping": {
"telephoneNumber": {
"outputPath": "$.telephoneNumber",
"responseBodyPath": "$.telephoneNumber",
"type": "responseBody"
}
}
}
This will perform the same request as above in the API Connector, but instead go out via an VPC and NAT.
The private-soap connector is used to hit HTTP endpoints, via VPC and NAT so it always comes from a known host ip address. For example:
{
"Alias": "MyPrivateSoapConnector",
"Type": "private-soap",
"Endpoint": "https://graphical.weather.gov:443/xml/SOAP_server/ndfdXMLserver.php",
"Body": {
"_declaration": {
"_attributes": {
"version": "1.0"
}
},
"soapenv:Envelope": {
"_attributes": {
"xmlns:ndf": "https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl",
"xmlns:soapenv": "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:xsd": "http://www.w3.org/2001/XMLSchema",
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance"
},
"soapenv:Body": {
"ndf:CornerPoints": {
"_attributes": {
"soapenv:encodingStyle": "http://schemas.xmlsoap.org/soap/encoding/"
},
"sector": {
"_attributes": {
"xsi:type": "xsd:string"
}
}
}
}
}
},
"Headers": {
"Content-Type": "text/xml"
},
"Method": "POST",
"RequestMapping": {
"sector": {
"inputPath": "$.sector",
"requestBodyPath": "$[\"soapenv:Envelope\"][\"soapenv:Body\"][\"ndf:CornerPoints\"][\"sector\"][\"_text\"]",
"type": "requestBody"
}
},
"ResponseMapping": {
}
}
For reference, here is the XML SOAP request and response.
A POST is performed to https://graphical.weather.gov:443/xml/SOAP_server/ndfdXMLserver.php
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ndf="https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl">
<soapenv:Header/>
<soapenv:Body>
<ndf:CornerPoints soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<sector xsi:type="xsd:string">conus</sector>
</ndf:CornerPoints>
</soapenv:Body>
</soapenv:Envelope>
And the response is :
<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:CornerPointsResponse xmlns:ns1="https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl">
<listLatLonOut xsi:type="xsd:string"><?xml version='1.0'?><dwml version='1.0' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:noNamespaceSchemaLocation='https://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd'><minResolution>304.000000</minResolution><latLonList>20.191999,-121.554001 20.331773,-69.208160 50.105547,-60.885558 49.939721,-130.103438</latLonList></dwml></listLatLonOut>
</ns1:CornerPointsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The aws-sdk BaaS connector allows you to directly call a functionality within the AWS SDK.
{
"Alias": "detectSentiment",
"Body": {},
"Credentials": "srn:vault::goes::here",
"Type": "aws-sdk",
"Endpoint": "Comprehend",
"Method": "detectSentiment",
"RequestMapping": {
"LanguageCode": {
"inputPath": "$.LanguageCode",
"requestBodyPath": "$.LanguageCode",
"type": "requestBody"
},
"Text": {
"inputPath": "$.Text",
"requestBodyPath": "$.Text",
"type": "requestBody"
}
},
"Region": "eu-west-1"
}
aws-sdk BaaS entries require your AWS credentials in order to function. They should be saved as a Secret, and then referenced by the Credentials
property.
The Endpoint
field maps to the name of the AWS service you are calling in the aws-sdk. In this example, we’re using the Comprehend service; the Method
is the function to call in that service, which is detectSentiment in this case.
The following API connector is used to make a request to an endpoint that performs basic mathmatical calculations.
{
"Method": "POST",
"Endpoint": "https://a5cw4kf88j.execute-api.eu-west-1.amazonaws.com/hedev/soap-on-a-rope/wsdl",
"Headers": {
"Content-Type": "text/xml"
},
"Alias": "somename",
"Organization": "yourorg",
"Updated": 1574174250115,
"RequestMapping": {
"valB": {
"type": "requestBody",
"requestBodyPath": "$[\"soapenv:Envelope\"][\"soapenv:Body\"][\"AddFunctionRequest\"][\"valB\"][\"_text\"]",
"inputPath": "$.valB"
},
"valA": {
"type": "requestBody",
"requestBodyPath": "$[\"soapenv:Envelope\"][\"soapenv:Body\"][\"AddFunctionRequest\"][\"valA\"][\"_text\"]",
"inputPath": "$.valA"
}
},
"ResponseMapping": {},
"Type": "soap",
"Body": {
"_declaration": {
"_attributes": {
"version": "1.0"
}
},
"soapenv:Envelope": {
"_attributes": {
"xmlns:soapenv": "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:tem": "http://tempuri.org/"
},
"soapenv:Body": {
"AddFunctionRequest": {
"valA": 0,
"valB": 0
}
}
}
},
"Created": 1574174250115,
"Srn": "srn:baas:eu-private-3:engjoekelly:api-connector:soap"
}
This endpoint’s service has 4 possible functions, but for this example we are only testing AddFunctionRequest
. SubtractFunctionReques, multiplyFunctionRequest etc are also available. The structure of the XML request to this soap service is as follows:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:AddFunctionRequest>
<tem:valA>2</tem:valA>
<tem:valB>2</tem:valB>
</tem:AddFunctionRequest>
</soapenv:Body>
</soapenv:Envelope>
The soapenv:Envelope
section in the body
of the API connector replicates this request and is converted to XML before being sent to the endpoint
. In this case it is ok to omit the tem:
namespace. For demonstration purposes, here we will provide an execution json file, but in practice, a bot or worker will utilise the API connector. Note that the values of 0 in the body of the API connector are default values to be replaced by the values in the execution file.
{
"Alias": "soap",
"valA": 2,
"valB": 2
}
valA
in the execute json file is mapped to valA
in the body of the API connector using the requestBodyPath
:
"$[\"soapenv:Envelope\"][\"soapenv:Body\"][\"AddFunctionRequest\"][\"valA\"][\"_text\"]"
and likewise valB
in the execute json file is mapped to valB
in the body of the API connector using the requestBodyPath
:
"$[\"soapenv:Envelope\"][\"soapenv:Body\"][\"AddFunctionRequest\"][\"valB\"][\"_text\"]"
valA
and valB
are retrieved from the execute json file via the inputPaths “$.valA” and “$.valB”.
The unmapped response from this request is as follows:
{
"response": {
"xml": "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:tns=\"http://tempuri.org/\" xmlns:tm=\"http://microsoft.com/wsdl/mime/textMatching/\"><soap:Body><AddFunctionResponse xmlns=\"http://tempuri.org/\"><result>4</result></AddFunctionResponse></soap:Body></soap:Envelope>",
"json": {
"_declaration": {
"_attributes": {
"version": "1.0",
"encoding": "utf-8"
}
},
"soap:Envelope": {
"_attributes": {
"xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:tns": "http://tempuri.org/",
"xmlns:tm": "http://microsoft.com/wsdl/mime/textMatching/"
},
"soap:Body": {
"AddFunctionResponse": {
"_attributes": {
"xmlns": "http://tempuri.org/"
},
"result": {
"_text": "4"
}
}
}
}
}
},
"statusCode": 200,
"duration": 256
}
However this may be a bit unwieldly, so if we’re only interested in a portion of this response, we can do some response mapping to remedy this. So replace:
"ResponseMapping": {}
with…
"ResponseMapping": {
"result": {
"type": "responseBody",
"responseBodyPath": "$[\"json\"][\"soap:Envelope\"][\"soap:Body\"][\"AddFunctionResponse\"][\"result\"][\"_text\"]",
"outputPath": "$.result"
}
}
This will extract the result from the responseBodyPath
(which mirrors the json path in the response before the response mapping) and map it to the key result
as stated in the outputPath
(i.e. “$.result”).
The resulting mapped response, as follows, extracts only the information that we are interested in.
{ response: { result: '4' },
statusCode: 200,
duration: 1288,
headers: {}
}
There are 5 different parameter types that can be passed:
You can see the examples below for an idea of what each one does.
Terminology
To fully understand the various properties that belong to each parameter type, it can be helpful to know about the meaning of the terms used:
new BaaS.API({
"RequestMapping": {
"convId": {
"type": "requestBody",
"inputPath": "$.token.contents.public.${convoId}.jwt",
"requestBodyPath": "$.users.${convoId}.jwt"
}
},
// every other field ...
});
// context
{
token: { contents: { public: { myconv: { jwt: 'theJwt' } } } },
convoId: 'myconv'
}
// outputs request body
{ users: { myconv: { jwt: "theJwt" } } }
The Request body also supports encoding of files that are stored within s3. Currently only files uploaded via messenger into the gossip attachment folder are supported.
new BaaS.API({
"RequestMapping": {
"policyDoc": {
"encoding": "Base64", // type of encoding, currently only Base64 is supported
"inputPath": "$.fileUrl", // location of the image in the bucket
"requestBodyPath": "$.policyDoc",
"type": "requestBody"
}
},
// every other field ...
});
// context
{
fileUrl: 'https://servisbot-heupper-gossip.s3.eu-west-1.amazonaws.com/attachments/flowit/eu-west-1:123/7YCU-2qCF.jpg?'
}
// outputs request body
{
policyDoc: '<Base64 encoded string of the file>'
}
If for any reason you want to send some important info in the body, you can supply an SRN secret for this too.
new BaaS.API({
"body":{
"importantToken":"srn:vault:global:myorg:secret:my-secret-name:78",
},
"RequestMapping": {
},
// every other field ...
});
new BaaS.API({
"RequestMapping": {
"jwt": {
"type": "requestURL",
"inputPath": "$.token.contents.public.type",
"requestParameter": "type"
}
},
Endpoint: "http://www.servisbot.com/widgets/${type}"
// every other field ...
});
// context
{
token: { contents: { public: { type: 'myType'} } },
convoId: 'myconv'
}
// outputs
'http://www.servisbot.com/widgets/myType'
new BaaS.API({
"RequestMapping": {
"jwt": {
"type": "requestHeader",
"inputPath": "$.token.contents.public.jwt",
"requestParameter": "jwt"
}
},
"Headers": {
"Authorization": "Bearer ${jwt}",
"X-AMZ-Auth-${jwt}": "${jwt}"
//Headers can also contain an SRN now to hide Authorization tokens, i.e.
"Authorization": "srn:vault:global:myorg:secret:my-secret-name:78"
}
// every other field ...
});
// context
{
token: { contents: { public: { jwt: '1a2b3c'} } },
convoId: 'myconv'
}
// outputs headers
{ "Authorization": "Bearer 1a2b3c", "X-AMZ-Auth-1a2b3c": "1a2b3c" }
new BaaS.API({
"ResponseMapping": {
"jwt": {
"type": "responseBody",
"responseBodyPath": "$.token.contents.${somevalue}.jwt",
"outputPath": "$.users.jwt"
}
}),
// every other field ...
});
// output from the API
{
somevalue: 'public', // $.token.contents.${somevalue}.jwt gets replaced with $.token.contents.public.jwt
token: { contents: { public: { jwt: '1a2b3c'} } },
}
// outputs response body
{
"users": { "jwt": "1a2b3c" }
}
new BaaS.API({
"ResponseMapping": {
"jwt": {
"type": "responseHeader",
"responseHeaderKey": "content-type",
"outputPath": "$.contentType"
}
}),
// every other field ...
});
// response has header
`'content-type', 'application/json; charset=utf-8'`
// outputs
{
response: { ... },
status: 200,
headers: { contentType: "application/json; charset=utf-8" }
}
We also support the passing of certifications in the request. For this type of request, you need to add the Credentials field to the config of your API connector. This is supported for both public API connector and the Public SOAP connector requests. This Credentials field will contain the SRN of a secret. You can create the secret using the cli and the sb-cli secret ci
command. For example:
{
"Method": "POST",
"Endpoint": "https://httpbin.org/post",
"Credentials": "srn:vault::youOrg:certificate:secretName",
"Headers": {},
"Alias": "apiRequestWithCert",
"RequestMapping": {},
"ResponseMapping": {},
"Type": "api-connector",
"Body": {}
}