1.2.0 • Published 2 years ago

uask-sys v1.2.0

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

U-ASK

U-ASK Management System

This repository contains the server and the client of the U-ASK system. Before going further with this setup guide be sure to have been through U-ASK introduction and demo.

The server offers a GraphQL and a REST API. The client allows application to interact with the server by easing authentication and queries.

Authentication is implemented in a separate repository : U-ASK Authentication.

Note: npm scripts needs bash ; please configure git bash as script shell for npm on Windows.

Install the server

git clone https://github.com/u-ask/uask-sys.git

Server configuration

variabledescriptionexample
APP_ENVthe execution environmentproduction
PORTthe port to listen to3000
CALLBACK_ROOT_URLthe authorized open id callback URLhttps://uask.example.com/callback
AUTH_URLthe public URL of the authentication servicehttps://uask-api.example.com/oidc
AUTH_JWKSa JSON stringified JWKS{"keys":[{"crv":"P-256",...
SAAS_MODEset to false to remove sign uptrue
DB_CONNSTRa knex connection string to a PostgreSQL db{"user":"postgres",host":"localhost",database":"postgres",password":"******"}
SENDGRID_API_KEYSENDGRID_SENDERa sendgrid authentication key and authorized email address
TWILIO_API_KEY_SIDTWILIO_API_KEY_SECRETTWILIO_ACCOUNT_SIDa twilio authentication key set

Setting AUTH_JWKS to {} will use a quick start development-only signing set. For more information about JWKS generation, see this example.

SAAS_MODE defaults to true, this will allow anybody to sign up to the system. SAAS mode is required for the first start to allow the first user to sign up. This first user will be able to create more users.

The APP_ENV variable must be set to production. The other possible values are development or demo (see U-ASK).

Sendgrid and / or Twilio account and keys are necessary for sending authentication codes.

Important: do not rely on the .env file in production environment ; set the environment variable at host level. The present guide assumes that this is the case.

Database initialization

npm run migrate

Starting the server

First start, SAAS mode is required to create the first user.

SAAS_MODE=true npm run start

In order to create a first user, run the cli.js script.

./cli.js "https://localhost:<port>" signup

A browser will open on the connexion page. Choose "Sign up" to create a user.

Note: from now, the server can be restarted with SAAS mode disabled. No one will be able to sign up ; new users will be created by an existing user.

Subsequent starts :

npm run start

If no error is logged to the console, the system is properly started. The easiest way to test is to use the U-ASK client.

Install the client

npm i uask-sys

Client usage

This script saves the survey and a sample. The survey is built using U-SAK Domain Model. Note that a survey name must contain a -.

// start.js

import { UaskClient } from "uask-sys/pkce";
import { builder, Sample } from "uask-dom";

const b = builder();

b.survey("First-Survey")
  .pageSet("Questionnaire").pages("Questions");
b.page("Questions")
  .question("Ok?", "__INCLUDED", b.types.yesno)
  .question("When:", "INCLUSION_DATE", b.types.date());

const survey = b.get();

const client = new UaskClient("http://127.0.0.1:3000");
await client.surveyDriver.save(survey);
await client.sampleDriver.save(survey, new Sample("Sample-001"));
await client.destroy();

Run the start.js script ; a browser will open asking to sign in or sign up.

The following script will read the study we have just created :

// check.js

import { UaskClient } from "uask-sys/pkce";

const client = new UaskClient("http://127.0.0.1:3000");
const survey = await client.surveyDriver.getByName("First-Survey");
console.log(survey);
await client.destroy();

This should print a Survey domain model object to the console.

Persistence identifiers

When a domain model object is saved into the persistent store, it is given some identifiers. For example a Participant has a unique code that is computed server side. Changes done server side are returned by the client and should be used to create an updated version of the saved domain model object.

import { UaskClient } from "uask-sys/pkce";
import { ParticipantBuilder } from "uask-dom";

const client = new UaskClient("http://127.0.0.1:3000");

const survey = await client.surveyDriver.getByName("First-Survey");
const sample = await client.sampleDriver.getBySampleCode(survey, "Sample-001");
const participant = new ParticipantBuilder(survey, "", sample).build();

const keys = await client.participantDriver.save(survey, participant);
const participantWithCode = participant.update(keys);
console.log(participantWithCode);
await client.destroy();

This prints a Participant domain model object to the console with a participantCode computed server side.

Saving a participant and its interviews :

import { UaskClient } from "uask-sys/pkce";
import { ParticipantBuilder } from "uask-dom";

const client = new UaskClient("http://127.0.0.1:3000");

const survey = await client.surveyDriver.getByName("First-Survey");
const sample = await client.sampleDriver.getBySampleCode(survey, "Sample-001");

const builder0 = new ParticipantBuilder(survey, "", sample);
builder0.interview("Questionnaire")
  .item("__INCLUDED").value(true)
  .item("INCLUSION_DATE").value(new Date());
const participant = builder0.build();
const interview = participant.interviews[0];

const participantKeys = await client.participantDriver.save(survey, participant);
const participantWithCode = participant.update(participantKeys);

const interviewKeys = await client.interviewDriver.save(survey, participantWithCode, interview);
const interviewWithNonce = interview.update(interviewKeys);
const builder1 = new ParticipantBuilder(survey, participantWithCode);
builder1.interview(interviewWithNonce);

const persistedParticipant = builder1.build();
console.log(persistedParticipant);
await client.destroy();

This prints a Participant domain model object to the console with a participantCode computed server side. The Interview contained in the Participant has a nonce property also computed server side.

Audit trail

All modification are tracked into the audit trail. The following snippet modify the patient created above and display all journalized modifications.

import { UaskClient } from "uask-sys/pkce";
import { ParticipantBuilder } from "uask-dom";

const client = new UaskClient("http://127.0.0.1:3000");

const survey = await client.surveyDriver.getByName("First-Survey");
const samples = await client.sampleDriver.getAll(survey, "Sample-001");
const participant = await client.participantDriver.getByParticipantCode(survey, samples, "000002")
const interview = participant.interviews[0];

const builder = new ParticipantBuilder(survey, participant);
builder.interview("Questionnaire", interview.nonce)
  .item("__INCLUDED").value(false)
  .item("INCLUSION_DATE").value(new Date());
const updatedParticipant = builder.build();

await client.participantDriver.save(survey, updatedParticipant);
const records = await client.auditDriver.get(survey, {participantCode: "000002"})

console.log(records);
await client.destroy();

The second argument of auditDriver.get(survey, {participantCode: "000002"}) may take additional information to narrow the audit target. See auditDriver reference

generateDSL

The generateDSL function takes a Survey ans returns a string that contains the code used to build the survey using DSL builders as defined in U-ASK Domain Model.

import { UaskClient, generateDSL } from "uask-sys/pkce";

const client = new UaskClient("http://127.0.0.1:3000");

const survey = await client.surveyDriver.getByName("First-Survey");
const dsl = generateDSL(survey);
console.log(dsl);

The snippet above regenerates the code used to build the example used in this guide, see Client usage above.

Client reference

For more information on the domain object model, see U-ASK Domain Model.

UaskClient.surveyDriver

methodreturnsdescription
getByName(name: string)Promise<Survey>get a survey by its name
save(survey: Survey)Promisesave a survey and all its components

UaskClient.sampleDriver

methodreturnsdescription
getAll(survey: Survey)Promise<Sample[]>get all samples for given survey
getBySampleCode(survey: Survey, code: string)Promise<Sample>get a sample given its code
save(survey: Survey, sample: Sample)Promisesave a sample for the given survey

UaskClient.participantDriver

methodreturnsdescription
getAll(survey: Survey)Promise<Participant[]>get all participants for given survey
getBySample(survey: Survey, sample: Sample)Promise<Participant[]>get all participants for given sample
getByParticipantCode(survey: Survey, code: string)Promise<Participant>get a participant given its code
save(survey: Survey, participant: Participant)Promisesave a participant
delete(survey: Survey, participant: Participant)Promisedelete a participant

UaskClient.interviewDriver

methodreturnsdescription
save(survey: Survey, participant: Participant, interview: Interview)Promisesave an interview
delete(survey: Survey, participant: Participant, interview: Interview)Promisedelete an interview

UaskClient.participantDriver

methodreturnsdescription
getAll(survey: Survey, sample?: Sample)Promise<ISummary[]>get lightweight summaries for participants

UaskClient.userDriver

methodreturnsdescription
getAll(survey: Survey)Promise<User[]>get all users for given survey
getByUserId(survey: Survey, userid: string)Promise<User>get a user given its userid
save(survey: Survey, user: User)Promisesave a user for the given survey

UaskClient.auditDriver

methodreturnsdescription
get(survey: Survey, ref: AuditableRef)Promise<AuditRecord[]>get audit records

AuditableRef

propertydescription
patientCodeparticipant code
nonce?interview nonce
variableName?item variable name
intance?instance

Survey development life cycle

With U-ASK the recommended way to build surveys is to use DSL directly. It is recommended to create a new project with npm init, the add U-ASK dependencies:

npm install uask-dom uask-sys

Initialize a new survey with name Second-Survey on server https://uask-api.example.com:

./node_modules/.bin/uask-cli "https://uask-api.example.com" init "Second-Survey" > second-survey.js

This will create a starter script that creates a minimal survey.

In order to create the survey on the server, run the script :

node ./second-survey.js

The code may also be generated from an existing survey, by replacing the operation name from init to generate or gen:

./node_modules/.bin/uask-cli "https://uask-api.example.com" gen "Second-Survey" > second-survey.js

Survey code should be maintained in a software version control system like Github.