Sockets
There might be rare occasions where you need to extend the existing socket interface; e.g. after adding a new database table to CARE and you want to make this data available to the frontend. In this chapter we outline the steps necessary for either extending an existing socket or creating an entirely new one.
Note
Please also see the code conventions for the socket interface in Code Conventions
Sockets in CARE
The socket architecture is realized in the backend; in the following all paths are provided relative to the
directory backend/webserver
including all relevant components.
During start-up of the webserver all sockets specified in the sockets
directory that extends the Socket
base class are loaded.
Each socket is specified in an individual .js
-file exporting a class that extends the Socket
base class. Hence, adding
a socket means creating such a new file and overriding the abstract class.
This base class (specified in Socket.js
) defines several convenience methods to communicate with the frontend, as
well as attributes and methods to interact with the web server class starting the respective socket. In the following
sections we elaborate which methods and attributes you should use or override to realize your own service.
Conceptually, a socket class encapsulates a socket-io connection established by the webserver, which you can use to listen to certain messages and which you can use to send back messages. For each frontend client that connects to the webserver, all socket classes are instantiated anew having an associated client id. This means, all messages you send and receive via the socket attribute are always specific to this one client connection; in other words, there is no state transfer between different connections or broadcasting possible. However, some of this functionality is made available through the server object – we address these options in the Extending a Socket section below.
Creating a Socket
Let’s say we want to add a new socket called TestSocket
to CARE. First, we add a new file test.js
to the
backend/webserver/sockets
directory that exports the new socket class:
const Socket = require("../Socket.js");
/**
* <doc>
*/
module.exports = class TestSocket extends Socket {
constructor(server, io, socket) {
super(server, io, socket);
}
}
While this would already add the socket automatically to CARE on restart of the webserver, you should always override
the init()
method of the base class. Here, you will add the message listeners; for now, we will just log that the
socket has been initialized.
/**
* HEADER BOILERPLATE...
*/
module.exports = class TestSocket extends Socket {
constructor(server, io, socket) {
super(server, io, socket);
}
init() {
this.logger.debug(`TestSocket was created for client ${this.socket.id}`);
}
}
In the next section we elaborate on how to populate a socket by message listeners and response functionality.
Extending a Socket
Let’s assume we want to extend a socket TestSocket
defined in ./backend/webserver/sockets/test.js
. Generally,
socket logic should be kept lean and easy. The primary responsibility of a socket is to forward a new client request
to another module for processing (e.g. the database interface), handle possible errors and send the results back to
the client. In the following we explain the possible use-cases for extending a socket
Listening to a New Message Type
Let’s say we want to listen to new message types testIncrement
and testReset
that provides a data object parameters.
For the sake of brevity we do not interact with the database, but write to a class attribute.
/**
* HEADER BOILERPLATE...
*/
module.exports = class TestSocket extends Socket {
constructor(server, io, socket) {
super(server, io, socket);
this.testVar = 0;
}
/* ... */
// method for updating the variable
updateTestVar(newVal){
this.testVar = newVal;
return this.testVar;
}
init() {
/* ... */
// listen to testIncrement
this.socket.on("testIncrement", (data) => {
try {
this.socket.emit("testResult", {success: true, val: this.updateTestVar(this.testVar + data.inc)});
} catch (e) {
this.logger.error(e);
}
});
// listen to testReset
this.socket.on("testReset", () => {
try {
this.socket.emit("testResult", {success: true, val: this.updateTestVar(0)});
} catch (e) {
this.logger.error(e);
}
});
}
}
Let’s decompose the steps to realize this example. First, we extend the init()
method adding event listeners
on the socket using the on(msg, callback)
function of the socket.io client. We then call the class method that
allows us to modify the state of the test variable (this.updateTestVar(...)
). Finally, we return the resulting
value to the client via emit(msg, data)
with the resulting value.
Error and Rights Management
When interacting with the database the key challenge is error handling, marshalling (i.e. the translation of the DB
data representation into a suitable format for the frontend) and rights management.
For now, we assume that we want to call an already defined database a model Test
specified in
./backend/db/models/test.js
and integrate this call into the above example.
Let’s also assume that only administrators are allowed to change this value.
Note
Please refer to the guide on how to extend the database and add interface methods in Database.
/**
* HEADER BOILERPLATE...
*/
module.exports = class TestSocket extends Socket {
/* ... */
// method for updating the variable
async updateTestVar(newVal){
// use base class method to check for admin rights
if (this.isAdmin()) {
try {
const result = await this.models["test"].updateById("x", {val: newVal});
this.socket.emit("testResult", {success: true, val: result});
} catch (e) {
this.socket.emit("testResult", {success: false, message: "Failed to update test!"});
this.logger.error("DB error while updating test" + JSON.stringify(e));
}
} else {
// respond with a negative message
this.socket.emit("testResult", {success: false, message: "User rights and argument mismatch"});
}
}
init() {
/* ... */
// listen to testIncrement
this.socket.on("testIncrement", async (data) => {
try {
await this.updateTestVar(this.testVar + data.inc);
} catch (e) {
this.logger.error(e);
}
});
// listen to testReset
this.socket.on("testReset", async () => {
try {
await this.updateTestVar(0);
} catch (e) {
this.logger.error(e);
}
}
}
}
Let’s decompose the example again! We first update the updateTestVar
to use the dbUpdateTest
function interfacing
the database. Before making the actual call to the database, we check whether the user is an admin through the base
class isAdmin()
method accessing the backend user information associated with the current connection. Because the
database query might fail, we add a try-catch-block around it. In case of an error at any stage, we send a negative
testResult back to the client.
Note
It is crucial that you do full error handling on the socket level, i.e. at some point all exceptions should have been caught by a catch block. Otherwise, the webserver can crash due to minor errors during database interaction.
Broadcasting Responses
To realize collaboration features, it might be desirable to join clients into rooms and multicast messages to these groups. In exceptional cases, even a broadcast might make sense. You should use these two options very sparingly, because they imply a lot of network traffic.
To join a client to a socketio room, you can simply use the join(name)
function on the socket object:
this.socket.join("roomName");
To multicast a message to a room, you need to access the io object of the base class.
this.io.to("roomName").emit("msg", data);
Testing
Please think about how to test your socket. In general, you should test the functionality in isolation. We refer to the section on Tests for more information on how to test your code.
Socket Communication Schema
In order to streamline socket communication, especially when interacting with the database, CARE defines a standardized way to register and handle socket events using the createSocket
method provided by ./backend/webserver/Socket.js
.
This schema helps to ensure consistent error handling, optional transaction safety, and frontend compatibility.
createSocket: How it works
The createSocket
method allows you to define a socket event with built-in support for:
Error handling
Optional transaction wrapping
Automatic frontend success/failure callbacks
Clean backend-to-frontend event emission after database changes
createSocket(eventName, func, options = {}, transaction = false)
- Arguments:
eventName
: Name of the socket event to registerfunc
: Async function handling the request, with parameters(data, options)
options
: Object passed through the socket pipeline; can contain any contextual data (e.g., transaction info, user session, metadata)transaction
: Iftrue
, wraps execution in a Sequelize DB transaction
Warning
If your handler function performs any database write operations (e.g., create, update), you must set transaction = true
and use the provided transaction object inside the function. Failing to do so can result in inconsistent or partial data states.
Note
The createSocket
function should always be called within the init()
method of socket classes located in ./backend/webserver/sockets/*
.
The handler function passed to it should be a method of the class itself, not an inline function or a function defined inside init()
.
See a full example of the usage of createSocket in Example Lifecycle.
Transaction Usage
- If your socket operation modifies data, set
transaction = true
. This will: Open a Sequelize transaction
Pass it to your function via
options.transaction
Automatically commit/rollback
Allow you to define afterCommit() behavior
init() {
this.createSocket("studySaveAsTemplate", this.saveStudyAsTemplate, {}, true);
}
/**
* Save the current study as a template (creates a new study with template: true).
*
* @param {object} data - The input data from the socket call.
* @param {number} data.id - ID of the study to duplicate as a template.
* @param {object} options - Context passed through the socket pipeline.
* @returns {Promise<object>} The newly created template study.
*/
async saveStudyAsTemplate(data, options) {
const study = await this.models["study"].getById(data.id);
if (this.checkUserAccess(study.userId)) {
const template = await this.models["study"].add({
...study,
id: undefined,
hash: undefined,
template: true,
}, {transaction: options.transaction});
// Code executed after transaction commit success (code only, without further DB changes!)
options.transaction.afterCommit(() => {
this.emit("studyRefresh", template);
});
return template; // The value sent back to the frontend via the socket callback
} else {
throw new Error("No permission to save study as template");
}
}
Note
Socket handler functions should always include clear JSDoc-style docstrings describing the expected data parameters, transaction context, and return type. For full documentation standards, refer to: Conventions and Paradigms.
For a detailed explanation of how to notify the frontend after a transaction, including automatic tracking (via
autoTable
) and manualafterCommit
hooks, see: Tracking DB Changes (afterCommit).
See a further example of the transaction usage in Example Lifecycle.
Transaction Failure Handling
If an error is thrown inside a socket using transaction = true
, the transaction will be automatically rolled back.
Warning
Do not call transaction.commit()
or transaction.rollback()
manually when using createSocket
.
These are handled automatically, and calling them yourself can lead to runtime errors — they can only be called once per transaction.
Try-Catch Behavior
- Using
createSocket
wraps your function call in an automatictry-catch
block. If an error is thrown: The transaction is rolled back (if enabled)
A callback with
success: false
and the error message is sent to the frontend
See the section above on createSocket: How it works for more details.
No need to write:
try {
...
} catch (e) {
...
}
Just throw an error inside your function:
if (!this.checkUserAccess(...)) {
throw new Error("Access denied");
}
Socket Callback Responses
When a socket event completes, a callback function on the frontend receives a standardized response object:
{
success: true, // true if the backend handler completed without errors
data: {...}, // the return value from the backend function (if successful)
message: "..." // an optional error message if an error was thrown
}
This structure is automatically handled by createSocket
:
If the backend handler throws an error,
success
is set tofalse
andmessage
contains the error.If the function returns a value, it is included as
data
andsuccess
is set totrue
.
The following example uses CARE’s global toast system to display success or failure. For full documentation on toast messages, see Toast Messages.
this.$socket.emit("studySaveAsTemplate", {id: 1}, (result) => {
if (!result.success) {
this.eventBus.emit("toast", {
title: "Template Save Failed",
message: result.message,
variant: "danger",
});
} else {
this.eventBus.emit("toast", {
title: "Success",
message: "Study saved as template",
variant: "success",
});
}
});
Tracking DB Changes (afterCommit)
In most cases, when using models with autoTable = true
, CARE will automatically track database changes
and push updates directly to the frontend. This includes creation, updates, and deletions — no need to manually emit a ...Refresh
event.
However, in special cases where autoTable
is not used or doesn’t apply, you may need to manually notify the frontend after a successful transaction. Examples include:
Writing files to disk
Returning plain objects or custom responses
Using models that do not have autoTable = true
In these cases, you can register a manual afterCommit hook on the transaction object:
const doc = await this.models["document"].add({
name: data.name,
type: data.type,
userId: this.userId
}, {transaction: options.transaction});
fs.writeFileSync(target, data.file);
options.transaction.afterCommit(() => {
this.emit("documentRefresh", doc);
});
Warning
In normal use cases (e.g., with autoTable = true
), you do not need to use `afterCommit()` or manually emit refresh events.
CARE tracks database changes automatically and handles frontend updates for you.
Example Lifecycle
Here is a full example showing how the backend and frontend work together when using createSocket
for creating a new document.
Frontend: emit event and handle result with a toast
this.$socket.emit("documentCreate", {
type: 1,
name: this.name,
}, (res) => {
if (res.success) {
this.$refs.createModal.close();
this.eventBus.emit("toast", {
title: "Success",
message: res.data, // This will be: "Document successfully created"
variant: "success",
});
} else {
this.$refs.createModal.waiting = false;
this.eventBus.emit("toast", {
title: "Error",
message: res.message, // This will be: "Missing required fields: name and type"
variant: "danger",
});
}
});
Backend: register the socket
init() {
this.createSocket("documentCreate", this.createDocument, {}, true);
}
Backend: handler function implementation
/**
* Create document (HTML)
* @param data {type: number, name: string}
* @param options
* @returns {Promise<object>}
*/
async createDocument(data, options) {
const doc = await this.models["document"].add({
name: data.name,
type: data.type,
userId: this.userId
}, {transaction: options.transaction});
options.transaction.afterCommit(() => {
// Backend log entry instead of manual frontend emit; frontend is auto-updated via autoTable
this.logger.info(`Document created (id: ${doc.id}) by user ${this.userId}`);
});
return "Document successfully created";
}
What happens internally:
createSocket listens for “documentCreate” on the socket
When the event is triggered from the frontend:
The handler
createDocument
runsA Sequelize transaction is opened and passed as
options.transaction
If the function completes without error:
The transaction is committed
The afterCommit hook logs a backend info message (no manual frontend emit is needed; updates are handled automatically via autoTable)
The frontend receives
{ success: true, data: "Document successfully created" }
A success toast is displayed using that message
If an error is thrown:
The transaction is rolled back
The frontend receives
{ success: false, message: "..." }
with the error messageAn error toast is displayed using that message
Socket functions
Annotation Socket Functions (annotation.js)
- class AnnotationSocket()
Handle all annotation through websocket
- AnnotationSocket.getAnnotationsByDoc(data, options)
Returns the annotations for a given document by its id.
- Arguments:
data – the input
data.documentId (number) – the id of the document to retrieve the annotations for
options – not used
- Returns:
Promise.<void> –
- AnnotationSocket.loadCommentsByAnnotation(annotationId)
Load all comments for a document by annotation
- Arguments:
annotationId (number)
- Returns:
Promise.<void> –
- AnnotationSocket.sendAnnotation(data, options)
Send an annotation to the client by id
- Arguments:
data (Object) – the input data from the frontend
options (Object) – not used
data.annotationId (number) – the id of the annotation
- Returns:
Promise.<*> –
- AnnotationSocket.updateAnnotation(data, options)
Updates the annotations in the database. If the provided annotation is a new annotation, it will be created in the database otherwise the existing entry is overriden.
- Arguments:
data (Object) – the input data from the frontend
options (Object) – containing transactions
options.transaction – the DB transaction
data.annotationId (number) – the id of the annotation to update
data.documentId (number) – the id of the document to update
data.studySessionId (number) – the id of the study session
data.studyStepId (number) – the id of the study step
data.tagId (number) – the id of the tag
data.selectors (string) – the selectors of the annotation
data.deleted (boolean) – indicates if the data is deleted
data.anonymous (boolean) – indicates if the data is anonymous
- Returns:
Promise.<void> –
App Socket Functions (app.js)
- class AppSocket()
Send data for building the frontend app
- AppSocket.sendData(data)
Sends the data stored under data.table.
- Arguments:
data (Object) – The input data from the frontend
data.table (String) – Table to send
data.filter (Object) – Filters
data.include (Object) – what data to include
- Returns:
Promise.<void> –
- AppSocket.sendDataByHash(data, options)
Send data by hash
- Arguments:
data (Object) – The input data from the frontend
data.hash (String) – The hash value
data.table (String) – Table to send the data from
options (Object) – Additional configuration parameter
- Returns:
Promise.<void> –
- AppSocket.sendInit(data, options)
Send all data needed for the frontend app for initialization
- Arguments:
data (Object) – The input data from the frontend
options (Object) – Sequelize transaction options
- Returns:
Promise.<void> –
- AppSocket.sendOverallSetting(data, options)
Send overall settings including user setting
- Arguments:
data (Object) – The input data from the frontend
data.key (String) – The key in the user setting table
data.value (String) – The value in the user setting table
options (Object) – Additional configuration parameter
- Returns:
Promise.<void> –
- AppSocket.sendSettings(sendToAll=false)
Send all settings to the client
- Arguments:
sendToAll (boolean) – broadcast to all clients
- Returns:
Promise.<void> –
- AppSocket.sendSystemRoles()
Sends all the roles CARE has from the DB.
- Returns:
Promise.<void> –
- AppSocket.sendTables()
Send tables data to the client for automatic table generation
- Returns:
Promise.<void> –
- AppSocket.sendUser()
Sends the user information for this user loaded from the db.
- Returns:
Promise.<void> –
- AppSocket.subscribeAppData(data)
Subscribe to app data
- Arguments:
data (Object) – The input data from the frontend
data.table (String) – The name of the table to subscribe to
- Throws:
Error –
If data.table is not provided
- Returns:
Promise.<void> –
- AppSocket.unsubscribeAppData(data, options)
Unsubscribe from app data
- Arguments:
data (Object) – The input data from the frontend
options (Object) – Additional configuration parameter
- Returns:
Promise.<void> –
- AppSocket.updateAppData(data, options)
Update data for a specific table to the client
- Arguments:
data (Object) – The input data from the frontend
options (Object) – Additional configuration parameter
options.transaction (Object) – Sequelize DB transaction options
- Returns:
Promise.<*> –
- AppSocket.updateData(data, options)
Updates data in the database
- Arguments:
data (Object) – The input data from the frontend
data.table (String) – The name of the table to update
data.data (Object) – New data to update
options (Object) – Additional configuration parameter
options.transaction (Object) – Sequelize DB transaction options
- Returns:
Promise.<void> –
Assignment Socket Functions (assignment.js)
- class AssignmentSocket()
Handle user through websocket
- AssignmentSocket.addReviewer(data, options)
Adds new sessions to a study.
- Arguments:
data (Object) – The data for adding reviewers.
data.studyId (number) – The ID of the study to which reviewers are to be added.
data.reviewer (Array.<number>) – An array of user IDs representing the reviewers to be added.
options (Object) – The options for transaction data
- Returns:
Promise.<void> – - A promise that resolves when the reviewers have been added to the study.
- AssignmentSocket.createAssignment(data, options)
Assigns a peer review task to a list of reviewers based on a given template.
- Arguments:
data (Object) – The data for assigning peer reviews.
data.assignment (Object) – The assignment object containing details of the assignment.
data.reviewers (Array) – An array of reviewer IDs who will be assigned to the peer review.
data.template (Object) – The template object containing the configuration for the peer review.
data.documents (Array) – The documents to be reviewed.
options (Object) – The option for transaction data.
- Throws:
Error Will throw an error if the assignment cannot be created.
- Returns:
Promise.<void> – A promise that resolves when the peer review has been assigned.
- AssignmentSocket.createAssignmentBulk(data, options)
Creates multiple assignments based on the provided data.
- Arguments:
data – The data for creating assignments.
data.template (Object) – The template to be used for the assignments.
data.selectedReviewer (Array.<Object>) – An array of reviewer objects to be assigned to the assignments.
data.selectedAssignments (Array.<Object>) – An array of assignment objects to be reviewed.
data.mode (String) – The mode of the assignment creation (i.e, role or reviewer)
data.documents (Array.<Array>) – List of document assignments
data.roleSelection (Object) – If the mode is role, the role selection object
data.reviewerSelection (Object) – If the mode is reviewer, the reviewer selection object
- Returns:
Promise.<void> –
- AssignmentSocket.getAssignmentInfoFromCourse(data)
Retrieve all the assignments a course has.
- Arguments:
data (Object) – The data required for getting the relevant assignment info.
data.options (Object) – The options object containing the API key and URL of the Moodle instance.
data.options.courseID (number) – The ID of the course to fetch users from.
data.options.apiKey (string) – The API token for the Moodle instance
data.options.apiUrl (string) – The URL of the Moodle instance.
- Returns:
Promise.<ArrayLike.<T>> –
Collab Socket Functions (collab.js)
- class CollabSocket()
Handle collaboration through sockets
- CollabSocket.updateCollab(data, options)
Updates the collaboration status in the database. If there is non existent on the given entity, it will create one, otherwise it will be updated.
- Arguments:
data (Object) – the input collab object
data.collabId (number) – the id of the collaboration if existent
options (Object) – containing the transaction
- Returns:
Promise.<void> –
Document Socket Functions (document.js)
- class DocumentSocket()
Handle all document through websocket
Loading the document through websocket
- DocumentSocket.addDocument(data, options)
Uploads the given data object as a document. Stores the given pdf file in the files path and creates an entry in the database.
- Arguments:
data (Object) – The data object containing the document details.
data.name (string) – The name of the document.
data.file (Buffer) – The binary content of the document.
data.userId (number) – The ID of the user who owns the document (optional).
data.isUploaded (boolean) – Indicates if the document is uploaded by an admin (optional).
options (Object) – The options object containing the transaction.
- Returns:
Promise.<void> –
- DocumentSocket.checkDocumentAccess(documentId)
Check if user has rights to read the document data
The user has access to the document if: - The document is public - The document is owned by the user - The user is an admin - The document is used in a study where the user is a participant
- Returns:
Promise.<boolean> –
- DocumentSocket.closeDocument(data, options)
Close the document and save it if necessary.
This method saves the document if there is no study session and removes the document from the list of open documents.
- Arguments:
data (object) – The data object containing documentId and studySessionId.
data.documentId (number) – The ID of the document to close.
data.studySessionId (number) – The ID of the study session,
options (object) – The options object.
- Returns:
Promise.<void> –
- DocumentSocket.createDocument(data, options)
Create document (html)
- Returns:
Promise.<void> –
- DocumentSocket.documentGetMoodleSubmissions(data, options)
Get Moodle submissions from an assignment
- Returns:
Promise.<ArrayLike.<T>> –
- DocumentSocket.editDocument(data, options)
Edits the document based on the provided data.
This method is called when the client requests to edit a document. It first checks if the user has access to the document, and if so, it applies the edits to the document and sends the updated document to the client.
- Arguments:
data (object) – {documentId: number, “ops” array consisting of [offset: number, operationType: number, span: number, text: string, attributes: Object]}
options (object) – the options for the transaction
- Returns:
Promise.<void> –
- DocumentSocket.getData(data)
Send document data to client And send additional data like annotations, comments, tags
- Arguments:
data (object) – {documentId: number, studySessionId: number}
- Returns:
Promise.<void> –
- DocumentSocket.getDocument(data, options)
Send a document to the client
This method checks if the user has access to the document and then retrieves and sends the document data. For HTML documents, it fetches and combines draft edits with the existing content before sending.
- Returns:
Promise.<({document: Document, deltas: Delta}|{document: Document, file: Buffer})> –
- DocumentSocket.getPreviousStepId(studyStepId)
Helper method to get the previous step ID for a given study step ID
- Arguments:
studyStepId (number) – The ID of the study step
- Returns:
Promise.<(number|null)> – - The ID of the previous study step, or null if not found
- DocumentSocket.loadDocument(filePath)
Load document delta from disk (for HTML documents)
This method reads the delta file from the disk and returns it as a Delta object.
- Arguments:
filePath (string)
- Returns:
Promise.<Delta> –
- DocumentSocket.openDocument(data, documentId, options)
Open the document and track it, if not already tracked
This method adds the document to the list of open documents, being tracked by the socket.
- Arguments:
data (object) – The data object containing the documentId.
documentId (number)
options (object) – The options object.
- Returns:
Promise.<void> –
- DocumentSocket.publishDocument(data, options)
Publish the document
- Arguments:
data (object)
data.documentId (number) – The ID of the document to publish.
options (object) – The options object containing the transaction.
- Returns:
Promise.<void> –
- DocumentSocket.publishReviewLinks(data)
Uploads review links to a Moodle assignment as feedback comments.
- Arguments:
data (Object) – The data required for uploading login data.
data.options (Object) – The options object containing the API key and URL of the Moodle instance.
data.options.courseID (number) – The ID of the course to fetch users from.
data.options.assignmentID (number) – The ID of the Moodle assignment.
data.options.apiKey (string) – The API token for the Moodle instance
data.options.apiUrl (string) – The URL of the Moodle instance.
data.feedback (Array.<Object>) – An array of objects containing the feedback to send
- Returns:
Promise.<Object> – - A promise that resolves when the passwords have been uploaded.
- DocumentSocket.refreshAllDocuments(data, options)
Refresh all documents
- Arguments:
data (Object) – The data object containing the request parameters.
options (Object) – The options object containing the transaction.
- Returns:
Promise.<void> –
- DocumentSocket.saveData(data, options)
Save additional document data for a particular document/study_session/study_step like the nlpResults, links etc., to the document_data table.
- Arguments:
data (*) – {userId: number, documentId: number, studySessionId: number, studyStepId: number, key: string, value: any}
options (*) – {transaction: Transaction}
- Returns:
Promise.<void> – - A promise that resolves when the data has been saved.
- DocumentSocket.saveDocument(documentId)
Save document delta to disk and mark edits as applied (for HTML documents)
This method saves the combined delta of the document on the disk and updates the edits in the database to mark them as applied.
- Arguments:
documentId (number)
- Returns:
Promise.<void> –
- DocumentSocket.sendByHash(data, options)
Send document by hash
- Arguments:
data (object)
data.documentHash (string) – The hash of the document to send.
options (object) – The options object containing the transaction.
- Returns:
Promise.<void> –
- DocumentSocket.sendDocumentDeltas(data, options)
Send merged deltas (from disk and database) to client (for HTML documents)
- Arguments:
data (object)
data.documentId (number) – The ID of the document to send deltas for.
options (object) – The options for the transaction.
- Returns:
Promise.<void> –
- DocumentSocket.subscribeDocument(data, options)
Subscribe to a document
- Arguments:
data (Object)
data.documentId (number) – The ID of the document to subscribe to.
options (Object) – The options object containing the transaction.
- Returns:
Promise.<void> –
- DocumentSocket.updateDocument(data, options)
Update a document
- Arguments:
data – The data object containing the new document object.
options – The options object containing the transaction.
- Returns:
Promise.<void> –
Logger Socket Functions (log.js)
- class LoggerSocket()
Handle logs through websocket
- LoggerSocket.getAllLogs(data, options)
Get log messages
- Arguments:
data – The log entry selection criteria
data.filter – Log entry filter
data.order – Log entry order
data.limit – Log entry limit
data.page – Log entry page
options – Unused
- Returns:
void –
- LoggerSocket.log(data, options)
Log a message
- Arguments:
data – The data to log
data.level – The log level
data.message – The log message
data.metadata – Optional log message metadata
options – Unused
- Returns:
void –
Service Socket Functions (service.js)
- class ServiceSocket()
Handling Services through websockets
- ServiceSocket.connectService(data, options)
Connects a service to the client
- Arguments:
data (Object) – The input data
data.service (string) – The name of the service to connect
data.data (Object) – Additional data to pass to the service
options (Object) – not used
- Returns:
Promise.<void> –
- ServiceSocket.disconnectService(data, options)
Disconnects a service from the client
- Arguments:
data (Object) – The input data
data.service (string) – The name of the service to disconnect
data.data (Object) – Additional data to pass to the service
options (Object) – not used
- Returns:
Promise.<void> –
- ServiceSocket.requestService(data, options)
Request a service (with default command)
- Arguments:
data (Object) – The input data
data.service (string) – The name of the service to disconnect
data.data (Object) – Additional data to pass to the service
options (Object) – not used
- Returns:
Promise.<void> –
- ServiceSocket.serviceCommand(data, options)
Request a service but with a specific command
- Arguments:
data (Object) – The input data
data.service (string) – The name of the service to disconnect
data.data (Object) – Additional data to pass to the service
options (Object) – not used
- Returns:
Promise.<void> –
Setting Socket Functions (setting.js)
- class SettingSocket()
Handle settings through websocket
- SettingSocket.saveSettings(data, options)
Save settings to the database
- Arguments:
data (Array.<{key: string, value: any}>) – List of settings to be saved
options (object) – Context passed through the socket pipeline
- Returns:
Promise.<string> – Success message
- SettingSocket.sendSettings(data, options)
Send current settings to the client
- Arguments:
data (any) – Unused
options (object) – Context passed through the socket pipeline
- Returns:
Promise.<Array.<{key: string, value: any}>> – All settings in flat key-value format
Statistic Socket Functions (statistic.js)
- class StatisticSocket()
Add statistics about the website usage
Therefore, use in frontend:
this.$socket.emit(“stats”, {action: “action”, data: {}});
The data object can hold additional information!
- StatisticSocket.addStats(data, options)
Add statistics
- Arguments:
data (Object) – The data object containing the userId
data.action (Number) – The type of action (e.g. ‘mouseMove’)
options (Object) – not used
- Returns:
Promise.<void> – - The statistics data
- StatisticSocket.getStats(data, options)
Get statistics
- Arguments:
data (Object) – The data object containing the userId
data.userId (Number) – The userId to get statistics for (optional)
options (Object) – not used
- Throws:
Error –
If the user does not have permission to access the data
- Returns:
Promise.<Object> – - The statistics data
- StatisticSocket.getStatsByUser(data, options)
Get a user’s statistics
- Arguments:
data (Object) – The data object containing the userId
data.userId (Number) – The user’s ID
options (Object) – not used
- Returns:
Promise.<void> – - The statistics data
- StatisticSocket.sendStatsByUser(userId)
Send statistics to the user
- Arguments:
userId (number)
- Returns:
Promise.<void> –
Study Session Socket Functions (study_session.js)
- class StudySessionSocket()
Handle all study sessions through websocket
Loading the study sessions through websocket
- StudySessionSocket.sendSessionsByStudyId(studyId)
Send all study sessions to the client
- Returns:
Promise.<void> –
- StudySessionSocket.startStudySession(data, options)
Start a study session
- Arguments:
data (object)
data.studyId (number) – A study id
data.studySessionId (number) – A study session id
options (object) – Transaction options
- Returns:
Promise.<void> –
- StudySessionSocket.subscribeToStudySession(data, options)
Subscribe to a study session
- Arguments:
data (object)
data.studyId (number) – A study id
options (object) – Transaction options
- Returns:
Promise.<void> –
- StudySessionSocket.unsubscribeFromStudySession(data, options)
Unsubscribe from a study session
- Arguments:
data (object)
data.studyId (number) – A study id
options (object) – Transaction options
- Returns:
Promise.<void> –
Study Socket Functions (study.js)
- class StudySocket()
Handle all studies through websocket
Loading the studies through websocket
- StudySocket.closeBulk(data, options)
Close a bulk of studies
- Arguments:
data.projectId – the project id of the studies to close
data.ignoreClosedState – if true, also close studies that are already closed
data.progressId – the id of the progress bar to update
options – not used
- Returns:
Promise.<void> –
- StudySocket.saveStudyAsTemplate(data, options)
Save the current study as a template (create a new study with template: true)
- Arguments:
data (object)
data.id (number) – the id of the study to save as template
options (object) – the options for the transaction
- Returns:
Promise.<*> –
User Socket Functions (user.js)
- class UserSocket()
Handle user through websocket
- UserSocket.bulkCreateUsers(data)
Bulk create or update users
- Arguments:
data (Object) – The data object containing the users and role map
- Returns:
Promise.<{createdUsers: Array, errors: Array}> – - An object containing the created users and errors
- UserSocket.checkUsersExists(data, options)
Check a list of users if they already exist in the database by email
- Arguments:
data – The data object containing the users to check - at least email key is required
options – The options object
- Returns:
- UserSocket.createUser(data, options)
Add a user to the database
- Throws:
Error –
If the user is not an admin
- Returns:
Promise.<*> –
- UserSocket.getUsers(role)
Get users by their roleu.status === “duplicate”
- Arguments:
role (string) – The role of the users to fetch. Possible values: “student”, “mentor”, “all”
- Returns:
Array.<string> – An array of users.
- UserSocket.getUsersByRole(data, options)
Get users by their role
- Arguments:
data (Object) – The input data from the frontend
data.role (string) – The role of the user
options (Object) – Additional configuration parameter
options.transaction (Object) – Sequelize DB transaction options
- Throws:
Error –
If the user does not have right
- Returns:
Promise.<void> –
- UserSocket.getUsersFromCourse(options)
Retrieves users from a specified moodle course and returns the data as an array.
- Arguments:
options (Object) – The data object containing the course ID, Moodle URL and the API token.
options.courseID (number) – The ID of the course to fetch users from.
options.options.apiKey (string) – The API token for the Moodle instance
options.options.apiUrl (string) – The URL of the Moodle instance.
- Returns:
Promise.<*> – - The response from the RPC service
- UserSocket.minimalFields(user)
Shows only specific fields of a user
- Arguments:
user (object) – The user object
- Returns:
- UserSocket.resetUserPwd(data, options)
Reset user’s password
- Arguments:
data (Object) – The input data from the frontend
data.userId (number) – The ID of the user
data.password (string) – The new password
options (Object) – Additional configuration parameter
options.transaction (Object) – Sequelize DB transaction options
- Throws:
Error –
If the user is not an admin or the user tries to reset other’s password
- Returns:
Promise.<void> –
- UserSocket.updateCreatorName(data, key="userId", targetName="creator_name")
Adds the username as creator_name of a database entry with column creator
Accepts data as a list of objects or a single object Note: Always returns a list of objects
- Arguments:
data (object|Array.<object>) – The data to update
key (string) – The key of the user ID field
targetName (string) – The name of the target field
- Returns:
- UserSocket.updateUserConsent(data, options)
Update user’s consent data
- Arguments:
data (Object) – The consent data object
data.acceptTerms (boolean) – Indicates whether the user has consented to the terms of service
data.acceptStats (boolean) – Indicates whether the user has agreed to tracking
data.acceptDataSharing (boolean) – Indicates whether the user has agreed to donate their annotation data
data.acceptedAt (string) – Time when the user made the consent
options (Object) – Sequelize transaction options
- Returns:
void –
- UserSocket.userPublishMoodle(data)
Uploads login data to a Moodle assignment as feedback comments.
- Arguments:
data (Object) – The data required for uploading login data.
data.options (Object) – The options object containing the API key and URL of the Moodle instance.
data.options.courseID (number) – The ID of the course to fetch users from.
data.options.assignmentID (number) – The ID of the Moodle assignment.
data.options.apiKey (string) – The API token for the Moodle instance
data.options.apiUrl (string) – The URL of the Moodle instance.
data.users (Array.<Object>) – An array of objects containing the uploaded users.
- Returns:
Promise.<Object> – - A promise that resolves when the passwords have been uploaded.
Comment Socket Functions (comment.js)
Loading the comments through websocket
Add a comment
data (object) – comment object
options (object) – contains the transactions
Promise.<void> –
Add or update a comment if it has already existed
data (object) – The input data from the frontend
data.commentId (number) – The id of the comment
options (object) – holds the transaction
Promise.<void> –
Get all the comments of a certain document
data (object) – The input data from the frontend
data.documentId (number) – The id of the document
options (object) – not used
Promise.<void> –
Send a comment to the client
data (object) – The input data from the frontend
data.commentId (number) – The id of the comment
options (object) – not used
Promise.<void> –
Update comments
data (Object) – containing the inputs
data.commentId (number) – the id of the comment
data.comment (object) – the comment object
options (object) – containing transaction
Promise.<void> –