SP Twilio Sync Client
This is an npm package that is a wrapper of Twilio Sync to be used by SmarterServices. It provides convenience functions for storing and setting data in the sync doc, as well as emitting custom events.
Installation
This is a private NPM package for smarterservices, so to install the dependency for your project, add this to the package.json
dependencies
"@smarterservices/twilio-sync-client": "1.0.0",
Then it is a simple import to use it in react
import {TwilioSyncClient, ActionTypes} from '@smarterservices/twilio-sync-client';
As you can see, we are going to import both TwilioSyncClient
and ActionTypes
.
Instantiating the TwilioSyncClient
Here is a code example of how to create an instance of our TwilioSyncClient
.
let twilioSyncClient;
new TwilioSyncClient({
baseUri: `${config.apiUrl}`,
documentId: `Student_${sessionStart.session.sid}`,
identity: `${sessionStart.session.sid}`,
token: `${validSpToken}`,
initialData: syncDocInitialData
}).then((client) => {
twilioSyncClient = client;
});
Because this client has to work with older versions of Webpack, the constructor looks a little different. In our old UI
we cannot use async/await
, so the client returns a version of itself as a Promise. In newer versions of react/Webpack,
this instantiation can look different to wait for the Promise to resolve.
Required Fields:
-
baseUri
: This is the base URI pointing to the SP API - example) 'https://api.dev.smarterproctoring.com/v1' -
documentId
: This is the unique name for the sync document.- For a session sync doc we do: "Session_" +
sessionSid
- ex). "Session_ES12345678901234567890123456789012"
- For a proctor room sync doc we do: "Proctor_Room_" +
proctorRoomSid
- ex). "Proctor_Room_PR12345678901234567890123456789012"
- For a session sync doc we do: "Session_" +
-
identity
: This is the identity for the sync client, which has to be unique across ALL clients. So, the identity is different based on the user AND what doc they are joining- For a student, they can only connect to their own session sync doc, so their identity is just their
sessionSid
- ex). "ES12345678901234567890123456789012"
- For a proctor, they can join session sync docs and proctor room sync docs, so they have identities for both situations
- Proctor Connecting to Session Sync Doc: Identity is:
sessionSid
+proctorAccountUserSid
- ex). "ES12345678901234567890123456789012PU12345678901234567890123456789012"
- Proctor Connecting to Proctor Room Sync Doc: Identity is:
proctorAccountUserSid
- ex). "PU12345678901234567890123456789012"
- Proctor Connecting to Session Sync Doc: Identity is:
- For a student, they can only connect to their own session sync doc, so their identity is just their
-
token
: This is a valid SP token that users are granted from launching in (LTI, Lighthouse, etc)
Optional Fields:
-
initialData
: This is a JSON object that you would like to put into the sync doc upon creation- ex).
{ reliableConnection: true }
- ex).
Getting and Setting values in the Sync Doc
Once you have a SyncClient instantiated, it wraps functionality for the specific document that you passed in. These functions all return Promises, so we have to call them accordingly.
So, if you wish to receive the current data in the sync doc, you would run the following:
twilioSyncClient.getSync()
.then(data => {
// do whatever with the data here
});
If you wish to set data in the Sync Doc, you can run the following:
const objectToPush = {
firstField: 'Hey There!',
secondField: 'This is probably good enough for an example'
}
twilioSyncClient.setSync(objectToPush)
.then(success => {
// do something if it was or was not succesfully updated
});
Custom Events
Subscribing
When the sync doc is updated, we will raise events to whoever is subscribed. Below is a list of the events that we raise, as well as a code sample on how to listen for the particular event
-
spSyncDocUpdated
: This is an event that will return the sync doc in its entirety every time it is updated.- How to subscribe:
twilioSyncClient.getEventEmitter().on("spSyncDocUpdated", function(data) { // Do something with the sync doc data here });
- How to subscribe:
-
extensionActionsAdded
: This is an event that will return the action object to a subscribed user. If there is a subscribed user, then the action will be deleted from the array, otherwise they will remain in the array.- How to subscribe:
twilioSyncClient.getEventEmitter().on("extensionActionsAdded", function(data) { // Do something with the action object here });
- How to subscribe:
-
onboardingActionsAdded
: This is an event that will return the action object to a subscribed user. If there is a subscribed user, then the action will be deleted from the array, otherwise they will remain in the array.- How to subscribe:
twilioSyncClient.getEventEmitter().on("onboardingActionsAdded", function(data) { // Do something with the action object here });
- How to subscribe:
Creating
Whenever we update the sync doc, we will create the action for spSyncDocUpdated
, so we will not go into detail about that
event here. However, we do need to talk about "Actions". Actions are simple JSON objects that are put into one of our "Action Arrays".
As of writing this README, we have onboardingActions
, which are actions that the onboarding UI needs to execute, and we have
extensionActions
, which are actions that the extension needs to take.
For example, if I am writing a piece of functionality on the onboarding UI that needs to have the extension inject its password,
then I can do that by passing an extensionAction
to tell the extension to inject a password. Below is a code sample about
how that action would be created.
twilioSyncClient.addAction(ActionTypes.extension.injectPassword);
As you can see, we will use the ActionTypes
object that we import to find the available actions. From the ActionTypes
object, you can look at extension actions with ActionTypes.extension
or onboarding actions with ActionTypes.onboarding
.
Sometimes actions may take some additional information.
For example, we can tell the extension that we need to end the session, which we can do like this:
twilioSyncClient.addAction(ActionTypes.extension.stopSession);
However, let's say that we also want to redirect the user to a different URL than the default redirect. We can now add
some additional information to this action. As a second param, the addAction()
function can take an optional JSON object
that can be used in some actions. Also, some actions require additional information as the extension will not have all of
the information needed to complete all of its actions.
So, if I wanted to tell the extension to end the session and to redirect the user to our end-session
screen for automated
students. Then I could accomplish that by the following:
twilioSyncClient.addAction(ActionTypes.extension.stopSession, { redirectURL: 'https://app.smarterproctoring.com/#/automated/end-session' });
If you ever want to see what fields are required for an action, look at the ActionTypes
file in the client, and any field
that is null in the desired action is a field that is MANDATORY to send in.
Sometimes we may need to add more than one action at a time. In this case, it is not the best practice to combine multiple
addAction
calls, but rather send a batch of actions. To do this, we can use addActions()
as detailed in the example below:
twilioSyncClient.addActions([
{
actionType: ActionTypes.extension.injectPassword
},
{
actionType: ActionTypes.extension.stopSession,
additionalInfo: { redirectURL: 'https://app.smarterproctoring.com/#/automated/end-session' }
}
]);
Here we can see that we take in the same two parameters as the single addAction()
call, but they are put as fields inside of an
array of objects. Make sure that you put the actions in the order of which you would like them to execute, because actions
should be taken as FIFO.
Responding
If you are sending an action that needs a response, we can use the respondTo
field. This tells the action taker where to post
the resulting data from the action. For instance, when the onboarding UI needs to get the number of displays from the extension,
it needs to have the extension run the action of getDisplayCount
, but also needs it to respond to the onboarding UI's action array.
So, to make this call from the UI, we would do the following:
twilioSyncClient.addAction(ActionTypes.extension.getDisplayCount, { respondTo: 'onboarding' });
As long as the UI is subscribed to the onboardingActionsAdded
event, and we see that we provided the respondTo
for a response
in the call above, then the UI will receive an action that will look something like this.
{
action: 'getDisplayCount',
executor: 'onboarding',
displayCount: 0
}