Scripts and source code
Stryke allows you to write your app's business logic in the form of simple scripts. These scripts are written in Javascript (ES2017). Your scripts run in a sandboxed Node environment and can benefit from many of the features that Node provides.
You can create and edit your scripts through the Stryke's web interface but we recommend you to Download Visual Studio Code and install our extention: Stryke Code. This provides a more complete experience and allows you to work with your scripts from your local environment.
Scripts can contain anything, the only requirement for a script to work is that it must be ended by either a call to the resolve
or error
methods.
// the most basic Stryke script!
stryke.resolve('Hello World!');
These methods are part of the Stryke Library.
Stryke Library
The Stryke Library provides ways to interact with Stryke from your scripts. It includes functions that can be called to query data, create and modify records, but also getting information about the execution context of the script, such as: the user that is executing the script, or the record from which an execution was triggered, and much more.
Below are examples of how to use the functions and properties provided by the Stryke Library.
For more details be sure to check out the Stryke Library Reference
Script Functionality
Resolve and Error
All scripts must be ended with a call to either
resolve
orerror
.
resolve
ends the execution of a script much like a return statement in a function. The resolve
function accepts an optional parameter, which is the value that will be returned to Stryke and ultimately shown on your app's UI or the response to an API request. The value returned through the resolve
function must match the datatype that the parent action is expected to return. For example, if the action's return type is: object
the resolve
function should be passed a JS object.
resolve
comes in handy when your code contains asynchronous calls. By placing the resolve
call in the right place you can ensure that the execution of the script will wait for your promises or callbacks to finish before ending the script execution.
myAsyncFunc((result) => {
// the script will end only when the callback is called!
stryke.resolve(result);
});
error
is similar to resolve
in the fact that it also ends script execution as soon as it is reached. The only difference is that the script will be terminated with an error. The error
function can accept a message, which will be returned and shown in the app's UI or API response. Calling error
is the correct way to handle error conditions inside your scripts.
myPromise.then((result) => {
stryke.resolve(result);
})
.catch((err) => {
// the script will be ended with an error response containing the message from 'err'
stryke.error(err.message);
});
Create and Update functions
The Stryke Library provides functions to create and update data in your app. These functions are asynchronous and will callback to your script with the result of the operation. Create and update can work in one of two ways:
- accepting a callback function which is called when the operation is completed (with or without an error)
- if no callback function is provided they will return a promise which is resolved or rejected with the result or error from the operation.
Create
Below is an example of how to use the create
function with a callback:
const receiptToCreate = { amount: 65.50, type: '8cd87440-06dd-454d-9346-6de44a6360f2' }
stryke.create('receipt', receiptToCreate, (err, savedRecord) => {
// if there is an error, terminate with it!
if (err) {
stryke.error(err.message);
}
console.log('saved: ' + savedRecord.alias);
stryke.resolve();
});
Alternatively, the same code can be implemented using a promise:
const receiptToCreate = { amount: 65.50, type: '8cd87440-06dd-454d-9346-6de44a6360f2' }
stryke.create('receipt', receiptToCreate).then((savedRecord) => {
console.log('saved: ' + savedRecord.alias);
stryke.resolve();
})
.catch((err) => {
// if there is an error, terminate with it!
stryke.error(err.message);
});
Since Stryke supports ES2017, we can make use of async/await to make this tidier:
const receiptToCreate = { amount: 65.50, type: '8cd87440-06dd-454d-9346-6de44a6360f2' }
try {
const savedRecord = await stryke.create('receipt', receiptToCreate);
console.log('saved: ' + savedRecord.alias);
stryke.resolve();
}
catch(err) {
// if there is an error, terminate with it!
stryke.error(err.message);
}
Update
Below is an example of how to use the update
function with a callback:
const receiptToUpdate = { amount: 50.50, type: '8cd87440-06dd-454d-9346-6de44a6360f2' }
stryke.update(idOfReceiptToUpdate, receiptToUpdate, (err, updatedReceipt) => {
if (err) {
stryke.error(err.message);
}
console.log('updated: ' + updatedReceipt.alias);
stryke.resolve(updatedReceipt);
});
Here is the same functionality as above, but implemented using a promise.
const receiptToUpdate = { amount: 50.50, type: '8cd87440-06dd-454d-9346-6de44a6360f2' }
stryke.update(idOfReceiptToUpdate, receiptToUpdate).then((updatedReceipt) => {
console.log('updated: ' + updatedReceipt.alias);
stryke.resolve(updatedReceipt);
})
.catch((err) => {
stryke.error(err.message);
});
Find, FindOne, and Query
Scripts can leverage Stryke's GraphQL API to perform advanced queries on the data. The Stryke Library offers three functions to query data.
Find
The stryke.find()
function accepts a GraphQL query string (this query can leverage all features and operators of the Stryke query) and returns a promise, which will resolve with an array of records containing the query result.
stryke.find()
should be the default function to use to query data. For additional functionality see the stryke.findOne()
and stryke.query()
functions below.
// A GraphQL query to retrieve all receipts where the amount is greater than 50.
// This query leverages Stryke's custom query engine through the 'filter' parameter.
const queryForReceipts =
`{
Receipt (filter: {
amount: { gt: 50 }
}) {
alias, amount, date, type
}
}`;
stryke.find(queryForReceipts).then(result) => {
stryke.resolve(`${result.length} receipts with amount > 50`);
}
FindOne
The stryke.findOne()
function provides a convenience function that wraps the stryke.find()
function and returns a single object instead of an array for queries that are expected to return a single value (example: query by ID). Note that this function will throw an error is the result contains more that 1 record.
// A GraphQL query to retrieve a single receipts record by ID
const queryForSingleReceipt =
`{
Receipt (id: "f3b94767-07ad-4d28-aa47-6266e88ea6c1") {
alias, amount, date, type
}
}`;
stryke.find(queryForReceipts).then(receipt) => {
stryke.resolve(`receipt: ${receipt.alias} with amount: ${receipt.amount}`);
}
Query
For more advanced query cases, the stryke.query()
function can be used instead of stryke.find()
or stryke.findOne()
.
stryke.query()
accepts more arguments than other query functions to provide more control over the query operation.
The result generated by the stryke.query()
function is more verbose than the format returned by stryke.find()
and follows the standard GraphQL format
stryke.query()
accepts a second argument to control whether access control should be applied when executing the query. Access control is always applied when using stryke.find()
or stryke.findOne()
. For scenarios in which it is necessary to retrieve data beyond what the current user has access to, the stryke.query()
function can be called passing false
as the second argument.
// Executes 2 queries in a single statement and returns a result in the standard GraphQL format.
const queryForReceipts =
`{
Receipt (filter: {
amount: { gt: 50 }
}) {
alias, amount, date, type
}
Type {
id, alias
}
}`;
stryke.query(queryForReceipts, true).then(result) => {
console.log('GraphQL errors: ' + result.errors);
stryke.resolve(`There are ${result.data.Receipt.length} receipts and ${result.data.Type.length} types`);
}
NOTE: It is possible to execute multiple query statements in a single query call using the stryke.find()
function too. In such cases the result returned by the stryke.find()
function will be the same as the one returned by stryke.query()
.
Execution Data
The Stryke Library offers access to ExecutionData. ExecutionData contains data that is relevant for a specific script execution.
As an example, when a script is triggered via a button on the app's UI, you can retrieve that record via stryke.data.record
.
stryke.data
and its content is provided to scripts as read-only. This means that objects like stryke.data.record
cannot be modified directly.
record
The record in context for this script execution. This is the record from which an script was triggered to run.
recordBefore
When running trigger scripts, recordBefore holds the data of the record that cause the trigger to run, before the update, create or delete actions were executed.
recordAfter
When running trigger scripts, recordAfter holds the data of the record that cause the trigger to run, after the update, create or delete actions were executed.
requestPayload
Any data that was passed as payload (ie the body of the HTTP request) to the original script or action execution request. This can be used to pass request specific arguments to the script being executed. The payload must be in valid JSON format.
User Info
UserInfo contains information about the app user that is executing the script. This allows retrieving the user's information via: stryke.user
.
Console
Scripts can use console
functions like any other JS code. The output of console.log
statements can be seen under Visual Studio Code, when executing a script via Stryke Code. Additionally they are stored by Stryke for auditing.
Supported Node Modules
Scripts runs in a sandboxed environment that guarantees security and data isolation. Not all
builtin
external modules
Script examples
Callout to a 3rd party service
Scripts can use node modules to perform requests to external services. This allows retrieving data as well as posting information. Here is a simple example illustrating a callout performed from a script.
Using axios
const axios = require('axios');
axios.get('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY')
.then((response) => {
// return the parsed JSON response
stryke.resolve(JSON.stringify(response.data));
})
.catch((error) => {
// handle errors
stryke.error('Callout failed: ' + error.message);
});
Using fetch
const fetch = require('node-fetch');
fetch('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY')
.then(res => res.json())
.then(json => stryke.resolve(JSON.stringify(json)))
.catch(error => stryke.error('Callout failed: ' + error.message));
Use Moment to work with dates
Moment is a very powerful module to work with dates and times in JS.
Below is a simple example that calculates the difference between two dates and returns a formatted message to the user.
const moment = require('moment');
// we are assuming that this action has a record in context (eg: a button clicked from
// a record), and that the record has a startDate and endDate date-time fields.
const momentStart = moment(stryke.data.record.startdate);
const momentEnd = moment(stryke.data.record.enddate);
const timeDifferenceInMinutes = momentEnd.diff(momentStart, 'minutes');
const formattedStart = momentStart.format('YYYY-MM-DD HH:mm');
const formattedEnd = momentEnd.format('YYYY-MM-DD HH:mm');
const formattedResult = `There are ${timeDifferenceInMinutes} minutes between ${formattedEnd} and ${formattedStart}`;
stryke.resolve(formattedResult);
Integrate with Slack
Slack provides a library called Bolt that provides ways to build Slack apps and interact with your Slack workspace.
For the full documentation on Bolt, check out Slack's official docs
The example below will post a message on the channel with the specified ID. In order for the script below to work you will need to create an app in your Slack workspace.
const { App } = require('@slack/bolt');
const SLACK_SIGNING_SECRET = 'set your signing secret here';
const SLACK_BOT_TOKEN = 'set your bolt token here';
const SLACK_CHANNEL_ID = 'set the channel id of the channel where you want to post the message';
// Initialize app
const app = new App({
signingSecret: SLACK_SIGNING_SECRET,
token: SLACK_BOT_TOKEN
});
// Slack error handler
app.error((error) => {
console.error(error);
stryke.error('failed: ' + error.message);
});
// post a message on your Slack channel
try {
app.client.chat.postMessage({
token: SLACK_BOT_TOKEN,
channel: SLACK_CHANNEL_ID,
text: 'Hello Slack!',
link_names: true
});
stryke.resolve('message posted on Slack!');
} catch (error) {
console.error(error);
stryke.error('failed: ' + error.message);
}