Example Concepts and Workflows
User Management
Overview
In the following example we will step through the API calls used to view all users, create a new user, and verify their creation.
Components
API Calls used
- GET /users
- POST /users/
Steps
First we will look at all our users using the GET /users
endpoint.
API Call
GET /users
Example API URL
https://snb.example.com/api/rest/v1.0/users?enabled=true&page%5Boffset%5D=0&page%5Blimit%5D=20
API Response Body
{
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users?enabled=true&page[offset]=0&page[limit]=20",
"first": "https://snb.example.com/api/rest/v1.0/users?enabled=true&page[offset]=0&page[limit]=20"
},
"data": [
{
"type": "user",
"id": "100",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/100"
},
"attributes": {
"lastLoginAt": "2023-07-26T14:54:47.714Z",
"createdAt": "2023-05-16T23:11:10.236Z",
"userId": "100",
"userName": "ross.geller@centralperk.com",
"email": "ross.geller@centralperk.com",
"firstName": "Ross",
"lastName": "geller",
"country": "USA",
"organization": "centralperk",
"isEnabled": true
},
"relationships": {
"roles": {
"data": [
{
"type": "role",
"id": "1",
"meta": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/1"
}
}
},
{
"type": "role",
"id": "4",
"meta": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/4"
}
}
},
{
"type": "role",
"id": "2",
"meta": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/2"
}
}
},
{
"type": "role",
"id": "3",
"meta": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/3"
}
}
}
]
},
"systemGroups": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/100/systemGroups"
}
}
}
}
],
"included": [
{
"type": "role",
"id": "3",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/3"
},
"attributes": {
"id": "3",
"name": "Standard User"
}
},
{
"type": "role",
"id": "4",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/4"
},
"attributes": {
"id": "4",
"name": "Inventory Admin"
}
},
{
"type": "role",
"id": "1",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/1"
},
"attributes": {
"id": "1",
"name": "System Admin"
}
},
{
"type": "role",
"id": "2",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/2"
},
"attributes": {
"id": "2",
"name": "Config Admin"
}
}
]
}
In our response we see all users. The data
object contains information about each user. In this case there is only one user, Ross Geller. Each user returned has their unique user attributes as an object as well as their related resources. In the case of a user these related resources are the roles associated with the user and their system groups.
To add a standard user we use the POST /users/
endpoint. We specify the required details as the body of our API request. We will use the information from our previous request to determine the id
and name
of the role for a Standard User
. Alternatively we could have just requested a full list of Roles
via GET /roles
.
API Call
POST /users/
Example of API URL
https://snb.example.com/api/rest/v1.0/users
API Request Body
{
"data": {
"attributes": {
"country": "USA",
"emailAddress": "rachel.green@centralperk.com",
"firstName": "Rachel",
"lastName": "Green",
"organization": "centralperk",
"roles": [
{
"id": "3",
"name": "Standard User"
}
]
}
}
}
API Response Body
{
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/101"
},
"data": {
"type": "user",
"id": "101",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/101"
},
"attributes": {
"createdAt": "2023-07-26T15:17:24.693Z",
"userId": "101",
"userName": "rachel.green@centralperk.com",
"email": "rachel.green@centralperk.com",
"firstName": "Rachel",
"lastName": "Green",
"country": "USA",
"organization": "centralperk",
"isEnabled": true
},
"relationships": {
"roles": {
"data": [
{
"type": "role",
"id": "3",
"meta": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/3"
}
}
}
]
},
"systemGroups": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/101/systemGroups"
}
}
}
},
"included": [
{
"type": "role",
"id": "3",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/3"
},
"attributes": {
"id": "3",
"name": "Standard User"
}
}
]
}
In the success response we see the details of our new user Rachel Green. Since she only has a single role our only included
information is the Standard User
role.
We can verify this further with another look at GET /users
using the same API request as earlier. Our response now contains two users, Ross and Rachel, and their relationships (roles in the case of users).
API Call
GET /users
Example API URL
https://snb.example.com/api/rest/v1.0/users?enabled=true&page%5Boffset%5D=0&page%5Blimit%5D=20
API Response Body
{
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users?q=r&enabled=true&page[offset]=0&page[limit]=20",
"first": "https://snb.example.com/api/rest/v1.0/users?q=r&enabled=true&page[offset]=0&page[limit]=20"
},
"data": [
{
"type": "user",
"id": "100",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/100"
},
"attributes": {
"createdAt": "2023-07-26T15:00:48.649Z",
"userId": "100",
"userName": "ross.geller@monh.com",
"email": "ross.geller@monh.com",
"firstName": "Ross",
"lastName": "geller",
"country": "USA",
"organization": "monh",
"isEnabled": true
},
"relationships": {
"roles": {
"data": [
{
"type": "role",
"id": "3",
"meta": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/3"
}
}
}
]
},
"systemGroups": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/100/systemGroups"
}
}
}
},
{
"type": "user",
"id": "101",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/101"
},
"attributes": {
"createdAt": "2023-07-26T15:17:24.693Z",
"userId": "101",
"userName": "rachel.green@centralperk.com",
"email": "rachel.green@centralperk.com",
"firstName": "Rachel",
"lastName": "Green",
"country": "USA",
"organization": "centralperk",
"isEnabled": true
},
"relationships": {
"roles": {
"data": [
{
"type": "role",
"id": "3",
"meta": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/3"
}
}
}
]
},
"systemGroups": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/101/systemGroups"
}
}
}
}
],
"included": [
{
"type": "role",
"id": "3",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/3"
},
"attributes": {
"id": "3",
"name": "Standard User"
}
},
{
"type": "role",
"id": "4",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/4"
},
"attributes": {
"id": "4",
"name": "Inventory Admin"
}
},
{
"type": "role",
"id": "1",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/1"
},
"attributes": {
"id": "1",
"name": "System Admin"
}
},
{
"type": "role",
"id": "2",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/roles/2"
},
"attributes": {
"id": "2",
"name": "Config Admin"
}
}
]
}
We now see Rachel as a user. There is no change to the included
data from our original response as her role was also present for Ross in the previous call.
Create experiment and update properties
Overview
In the following example we will step through the API calls used to create an experiment and subsequently update the description of the newly created experiment.
Components
API Calls used
- POST /entities/
- PUT /entities/{eid}/properties
Steps
To create an experiment we will use the POST /entities
endpoint. We will create a very basic fresh experiment named "My New Experiment".
Note: This experiment will show in the audit log of whichever user's authentication is used to make the API call. This is true for both API Key authorization and Bearer Token authorization.
API Request
POST /entities/
Example of API URL
https://snb.example.com/api/rest/v1.0/entities
API Request Body
{
"data": {
"type": "experiment",
"attributes": {
"name": "My New Experiment"
}
}
}
API Response Body
{
"links": {
"self": "https://snb.example.com/api/rest/v1.0/entities/experiment:52afbf7c-8dd2-4b5d-9060-07905f446370"
},
"data": {
"type": "entity",
"id": "experiment:52afbf7c-8dd2-4b5d-9060-07905f446370",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/entities/experiment:52afbf7c-8dd2-4b5d-9060-07905f446370"
},
"attributes": {
"id": "experiment:52afbf7c-8dd2-4b5d-9060-07905f446370",
"eid": "experiment:52afbf7c-8dd2-4b5d-9060-07905f446370",
"name": "My New Experiment",
"description": "",
"createdAt": "2024-02-05T20:04:48.419Z",
"editedAt": "2024-02-05T20:04:48.419Z",
"type": "experiment",
"state": "open",
"digest": "60367023",
"fields": {
"Description": {
"value": ""
},
"Name": {
"value": "My New Experiment"
}
},
"flags": {
"canEdit": true
}
},
"relationships": {
"createdBy": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/100"
},
"data": {
"type": "user",
"id": "100"
}
},
"editedBy": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/100"
},
"data": {
"type": "user",
"id": "100"
}
},
"owner": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/100"
},
"data": {
"type": "user",
"id": "100"
}
},
"pdf": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/entities/experiment:52afbf7c-8dd2-4b5d-9060-07905f446370/pdf"
}
}
}
},
"included": [
{
"type": "user",
"id": "100",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/100"
},
"attributes": {
"userId": "100",
"userName": "user.name@example.com",
"flags": {
"isSystemStandardUser": true
},
"email": "user.name@ example.com",
"firstName": "User",
"lastName": "Name",
"isEnabled": true
},
"relationships": {
"systemGroups": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/100/systemGroups"
}
}
}
}
]
}
In our successful response we see the basic data about our new experiment, including the name we set "My New Experiment", and can see additional details about the user who created the experiment.
Now we have decided that we want to add a description property via the API (this could have also been accomplished during creation but for our example we will be adding it immediately after the fact). We will accomplish this with the PUT /entities/{eid}/properties
API request. We will leverage the digest
value, 60367023
from our successful creation so that ensure we are not out of sync. Learn more about digests here.
NOTE: If the experiment we have just created is edited in any way between our initial creation and request to add a description the digest value will have changed. We can always get the latest digest by fetching our experiment using the GET /entities/{eid}
API Request
PUT /entities/{eid}/properties
Example API URL
https://snb.example.com/api/rest/v1.0/entities/experiment%3A52afbf7c-8dd2-4b5d-9060-07905f446370/properties?digest=60367023
API Request Body
{
"data": [
{
"attributes": {
"name": "Description",
"value": "This is the description of our new experiment"
}
}
]
}
API Response Body
{
"links": {
"self": "https://snb.example.com/api/rest/v1.0/entities/experiment:52afbf7c-8dd2-4b5d-9060-07905f446370/properties"
},
"data": [
{
"type": "property",
"id": "201616",
"meta": {
"definition": {
"type": "text",
"attribute": {
"id": "1",
"name": "Text",
"type": "text",
"counts": {
"templates": {}
}
},
"flags": {
"isRequired": true,
"canEdit": true
}
}
},
"attributes": {
"id": "201616",
"name": "Name",
"value": "My New Experiment",
"values": [
"My New Experiment"
]
},
"relationships": {
"attribute": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/attributes/1"
},
"data": {
"type": "attribute",
"id": "1"
}
},
"owner": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/entities/experiment:52afbf7c-8dd2-4b5d-9060-07905f446370"
},
"data": {
"type": "entity",
"id": "experiment:52afbf7c-8dd2-4b5d-9060-07905f446370"
}
}
}
},
{
"type": "property",
"id": "201617",
"meta": {
"definition": {
"type": "text",
"attribute": {
"id": "1",
"name": "Text",
"type": "text",
"counts": {
"templates": {}
}
},
"flags": {
"canEdit": true
}
}
},
"attributes": {
"id": "201617",
"name": "Description",
"value": "This is the description of our new experiment",
"values": [
"This is the description of our new experiment"
]
},
"relationships": {
"attribute": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/attributes/1"
},
"data": {
"type": "attribute",
"id": "1"
}
},
"owner": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/entities/experiment:52afbf7c-8dd2-4b5d-9060-07905f446370"
},
"data": {
"type": "entity",
"id": "experiment:52afbf7c-8dd2-4b5d-9060-07905f446370"
}
}
}
}
],
"included": [
{
"type": "attribute",
"id": "1",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/attributes/1"
},
"attributes": {
"id": "1",
"name": "Text",
"type": "text",
"counts": {
"templates": {}
}
}
},
{
"type": "entity",
"id": "experiment:52afbf7c-8dd2-4b5d-9060-07905f446370",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/entities/experiment:52afbf7c-8dd2-4b5d-9060-07905f446370"
},
"attributes": {
"type": "experiment",
"eid": "experiment:52afbf7c-8dd2-4b5d-9060-07905f446370",
"name": "My New Experiment",
"digest": "54905888",
"fields": {
"Description": {
"value": "This is the description of our new experiment"
},
"Name": {
"value": "My New Experiment"
}
}
}
}
]
}
In our success response we see the properties of our new experiment including our updated description
property.
Sample Registration Flow
Overview
In this example we will setup a Sample registration flow to be used directly from a Signals experiment. To accomplish this we will use an External action in conjunction with our REST API to collect sample information, provide a UI action to register the sample externally, and once registered update our Sample in signals with registration details.
Additionally we will incorporate workflow settings that ensure samples are both registered and up to date with the external LIMS.
Components
- Signals
- External Actions
- Signals REST APIs
- End User
- External Server setup
Prerequisites
- External Action for Sample entities is created and enabled
- External Actions
- The parameter name is sampleId
- An external server has been setup to handle External Action
- External Server setup
Workflow settings for signing experiments enabled by an administrator:
Architecture Diagram
API Calls used
GET /samples/{sampleId}/properties
GET /samples/{sampleId}/properties/digests.self
GET /samples/{sampleId}/properties/digests.external
PATCH /samples/{sampleId}/properties
PATCH /samples/{sampleId}/properties/digests.external
Steps
With our external action configured (named "Register with External LIMS" in our case) for our samples table we will take this action on a Sample to begin the workflow.
First we will take a look at an example sampleId
that is part of the URL from our External Action:
sample:6eaf9002-3bc7-4f9c-a2fc-bffdfb987cd7
In our external server we will use this to look up the properties of our sample. We will use these properties to both register our sample in our external LIMS as well as letting Signals know that we have done so. The later will ensure that all our samples are registered and up to date with our External LIMS as part of our signing workflow.
To start we look at the properties of our sample. We will use the full GET /samples/{sampleId}/properites
for demonstration purposes. You can specify specific parameters if you know the specific property you are interested in.
API Call:
GET /samples/{sampleId}/properties
Example API URL:
GET https://snb.example.com/api/rest/v1.0/samples/sample:6eaf9002-3bc7-4f9c-a2fc-bffdfb987cd7/properties
Partial API Response Body:
{
...
"data": [
{
"type": "property",
"id": "b718adec-73e0-3ce3-ac72-0dd11a06a308",
...
"attributes": {
"id": "b718adec-73e0-3ce3-ac72-0dd11a06a308",
"name": "ID",
"content": {
"value": "Sample-158"
}
},
...
},
...
{
"type": "property",
"id": "digests.self",
...
"attributes": {
"id": "digests.self",
"content": {
"value": "48ac6e467af8aebe152be89895cb0a81ada157bd632809528bc07c993b4bf753"
}
},
...
},
{
"type": "property",
"id": "digests.external",
...
"attributes": {
"id": "digests.external"
},
...
},
{
"type": "property",
"id": "sampleId",
...
"attributes": {
"id": "sampleId",
"name": "ID",
"content": {
"name": "Sample-158",
"type": "sample",
"value": "sample:6eaf9002-3bc7-4f9c-a2fc-bffdfb987cd7"
}
},
...
},
...
{
"type": "property",
"id": "5",
...
"attributes": {
"id": "5",
"name": "Chemical Name",
"content": {
"value": "benzocaine"
}
},
...
},
...
],
"included": [
...
]
}
In our partial JSON response body we are focusing on some key properties we will use to accomplish our workflow.
- digests.self
- This represents the current state of the Sample inside of Signals Notebook. Any change to the Sample will update this value.
- digests.external
- This represents the state of the Sample in our External System. Initially, and seen in our response, this is not set. Once we update this property it will be used to compare the state of the Sample in Signals Notebook compared to the External System.
- ID
- Chemical Name
The full response contains all the properties of our sample, as well as additional information in the included
array that may be required to register a Sample with your External LIMS. For example you may need the ID and Chemical Name to register with your External LIMS.
Once registered in your external LIMS we want to update our external digest of our Sample so that Signals knows that we have registered the Sample and what its state, or digest, was at the time of registration.
Our digest from the above example response:
48ac6e467af8aebe152be89895cb0a81ada157bd632809528bc07c993b4bf753
API Call:
PATCH /samples/{sampleId}/properties/digests.external
Example API URL:
PATCH https://snb.example.com/api/rest/v1.0/samples/sample:6eaf9002-3bc7-4f9c-a2fc-bffdfb987cd7/properties/digests.external
Example Request Body:
{
"data": {
"attributes": {
"content": {
"value": "48ac6e467af8aebe152be89895cb0a81ada157bd632809528bc07c993b4bf753"
}
}
}
}
At this point, the sample is considered registered and the UI will keep track of any changes made to the sample that would render the registration out of date, in which case the UI will show a warning. If we make any changes Signals will warn us (if configured to do so) that our Sample is out of date with our external system and needs to be re-registered externally by repeating the steps in this example.
Examples of warnings in the UI:
Automated Archival for Closed Experiments
Overview
In this example we will setup an automated process for generating PDF copies of experiments when they are closed. To accomplish this we will make use of our API and External Notifications. When setup an external notificaiton will fire for a "Sign and Close" event on an experiment. Once we recieve this we will kick of the process to generate a PDF, download it, and save it to a folder.
We will make use of the asynchronous PDF generation process. This is the recommended path for generation of PDFs to prevent time outs during the PDF generation of larger experiments.
Components
- Signals
- End User
Prerequisites
- External Notifications for "Sign and Close" events are enabled
- An external server has been setup to handle notifications
Architecture Diagram
API Calls Used
PUT /entities/export/pdf
HEAD /entities/export/pdf/{fileId}
GET /entities/export/pdf{fileId}
Steps
First in our external server we handle the JSON body of a notification to determine it is for a "Sign and Close" event and which experiment was closed. This external notification will provide us the starting point for our PDF generation.
To parse this out we will examine the JSON Object from the notification we receive.
JSON Notification Body
{
"links": {
...
},
"data": {
"type": "notification",
"id": "519",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/notifications/519"
},
"attributes": {
"id": "519",
"createdAt": "2023-12-13T20:42:50.427Z",
"type": "close",
"isDismissed": true,
"isRead": false,
"isFlagged": false,
"comment": ""
},
"relationships": {
"createdBy": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/users/100"
},
"data": {
"type": "user",
"id": "100"
}
},
"entity": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/entities/experiment:d5dc8e92-580d-41e2-9c65-99726294966c"
},
"data": {
"type": "entity",
"id": "experiment:d5dc8e92-580d-41e2-9c65-99726294966c"
}
}
}
},
"included": [
...
]
}
The first piece of information we want to extract is the type
from data
-> attributes
-> type
to very what type of notification we are receiving. For Sign and Close we are looking for close
.
Next we will extract the Experiments entity ID from data
-> relationships
-> entity
-> data
-> id
. In our example response this is:
experiment:d5dc8e92-580d-41e2-9c65-99726294966c
Now using our Experiments entity ID we will make an API call to begin the PDF generation. Our goal is to get the file id of the PDF being generated to use in subsequent API calls.
API Call
PUT /entities/export/pdf
Example API URL
https://snb.example.com/api/rest/v1.0/entities/export/pdf?eid=experiment:d5dc8e92-580d-41e2-9c65-99726294966c&attachments=true"
Partial API Response Body
{
"links": {
...
},
"data": {
"type": "pdf",
"id": "1b654ddf-17c7-4ec2-913c-9f9744358b4f",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/entities/export/pdf?eid=experiment:d5dc8e92-580d-41e2-9c65-99726294966c"
},
"attributes": {
"type": "pdf",
"fileId": "1b654ddf-17c7-4ec2-913c-9f9744358b4f",
"fileName": "f720-1.pdf"
},
"relationships": {
...
},
"included": [
...
]
}
From our response we parse out data
-> attribute
-> fileId
. In our example response: 1b654ddf-17c7-4ec2-913c-9f9744358b4f
Now with our file ID we will check to see if our PDF has completed generating. This is done by checking the content-length
from the HTTP header returned from our next API call. If the content-length
is > 0 we have completed generating our PDF, otherwise we will wait 5 seconds and try again.
API Call
HEAD /entities/export/pdf/{fileId}
Example API URL
https://snb.example.com/api/rest/v1.0/entities/export/pdf/1b654ddf-17c7-4ec2-913c-9f9744358b4f
Partial API Response Header
access-control-allow-credentials: true
access-control-allow-headers: Content-Type,Access-Control-Allow-Headers,Authorization,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,HEAD,DELETE,OPTIONS
cache-control: no-cache,no-store,must-revalidate
content-length: 35640
date: Tue,02 Jan 2024 20:38:32 GMT
expires: -1
pragma: no-cache
server: nginx
server-timing: intid;desc=efe2a92b4066b6f5
...
If the content-length
in our HEAD request response is > 0 we request to download the generated PDF with the API. Otherwise we repeat our HEAD request check for content-length
.
With our PDF generated all we need to do is download it and save it.
API Call
GET /entities/export/pdf{fileId}
Example API URL
https://snb.example.com/api/rest/v1.0/entities/export/pdf/1b654ddf-17c7-4ec2-913c-9f9744358b4f
This API will return a response with a content-type
of application/pdf
. Using whichever method appropriate for your external service you can download the PDF from the response.
Partial Response Header
...
content-disposition: attachment; filename="f720-1.pdf"; filename*=utf-8''f720-1.pdf
content-type: application/pdf
...
You have now configured your external server to asynchronously download any signed and closed experiments at the time of signing.
NOTE: To further ensure you never miss a "Sign and Close" notification read more about
Pull Notifications
Additional Signing Compliance
Overview
In this example we will setup additional compliance checking that may be unique to your organization. This will serve as a way to prevent signing and closing of experiments that are missing requirements. We will accomplish this with an External Action that opens an external site at the time of sign and close. For our example the additional compliance will verify that a safety document is included in the experiment. If it is included the page can be dismissed and signing will complete. Otherwise the external site will provide a warning to the end user that they need to add their safety document and when dismissed will prevent the sign and close event from completing.
Components
- Signals
- External Actions
- Signals REST APIs
- End User
- External Server setup
- External Site that presents a message based on the existence of an excel entity named "Safety Sheet"
- Utilizing External Actions Messages
Prerequisites
- External Action for "Sign and Close" events are enabled - Setup Guide
- An external site has been setup to handle our "Sign and Close" action
- External Server setup - The external site will warn the end user if they are missing required documentation
Architecture Diagram
API Calls Used
GET /entities/{eid}/children
Steps
With our external action configured for our "Sign and Close" on an Experiment we will look at the Experiments children during signing in order to check for an excel entity named "Safety Sheet". If successful we will show the user a success message and a continue button to dismiss our external site with closeAndContinue
. If we do not find the excel sheet we will display an error and dismiss our external site with closeAndAbort
.
First we will take a look at an example experiment EID that is part of the URL from our External Action:
experiment:33f3ce00-cdcb-4c52-8d25-6ce1b37134c6
In our external server we will use this to look up information of our Experiment. We will look for the children and verify there is an excel file and leverage the included
object to find a bit more information about the child, specifically its Name.
API Call
GET /entity/{eid}
Example API URL:
GET https://snb.example.com/api/rest/v1.0/entity/experiment:33f3ce00-cdcb-4c52-8d25-6ce1b37134c6
Partial API Response Body
{
...
"data": {
"type": "entity",
"id": "experiment:33f3ce00-cdcb-4c52-8d25-6ce1b37134c6",
...
"relationships": {
...
"children": {
...
"data": [
...
{
"type": "entity",
"id": "excel:5badd272-d234-4e2a-9c1f-c5a447ac0896",
"meta": {
"links": {
"self": "https://snb.example.com/api/rest/v1.0/entities/excel:5badd272-d234-4e2a-9c1f-c5a447ac0896"
}
}
},
...
]
},
...
}
},
"included": [
...
{
"type": "entity",
"id": "",
"links": {
"self": "https://snb.example.com/api/rest/v1.0/entities/excel:5badd272-d234-4e2a-9c1f-c5a447ac0896"
},
"attributes": {
"type": "excel",
"eid": "excel:5badd272-d234-4e2a-9c1f-c5a447ac0896",
"name": "Safety Sheet",
"digest": "56497474",
"fields": {
"Description": {
"value": ""
},
"Name": {
"value": "Safety Sheet"
}
}
}
},
...
]
}
In our partial response we can see the children
as part of the relationships
in the data
object. This contains all our Experiments children. We can look through this to find an excel
entity. In our example we see the following entity: excel:5badd272-d234-4e2a-9c1f-c5a447ac0896
We then can look for that entity within our included
array (alternatively we could make a second call to GET /entity/{eid}
with our excel
entities id
) for the name of our excel
file. If it is "Safety Sheet" we will allow the user to dismiss the external site and complete the signing using the External Action messages:
window.parent.postMessage(
['closeAndContinue', []],
'https://snb.example.com/'
)
This will complete the signing flow successfully and close the experiment.
In our example case we did find it, however, if we were to not find it we would dismiss our external site with the following message:
window.parent.postMessage(
['closeAndAbort', []],
'https://snb.example.com/'
)
This will abort the signing flow leaving our experiment open.