@levante-framework/firekit
Version:
A library to facilitate Firebase authentication and Firestore interaction for LEVANTE apps
135 lines (134 loc) • 6.2 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RoarAppUser = void 0;
const firestore_1 = require("firebase/firestore");
const extend_1 = __importDefault(require("lodash/extend"));
const interfaces_1 = require("../../interfaces");
const util_1 = require("../util");
/** Class representing a ROAR user */
class RoarAppUser {
/** Create a ROAR user
* @param {object} input
* @param {Firestore} input.db - The assessment Firestore instance to which this user's data will be written
* @param {string} input.roarUid - The ROAR ID of the user
* @param {string} input.assessmentUid - The assessment firebase UID of the user
* @param {string} input.assessmentPid - The assessment PID of the user
* @param {string} input.userType - The user type. Must be either 'admin', 'educator', 'student', 'caregiver', 'guest', or 'researcher.'
* @param {object} input.userMetadata - An object containing additional user metadata
* @param {string} input.testData = Boolean flag indicating test data
* @param {string} input.demoData = Boolean flag indicating demo data
* @param {string} input.offlineEnabled = Boolean flag indicating whether user has enrolled in Offline ROAR
* @param {string[]} input.offlineTasks = Array of task IDs that user will need access to offline
* @param {string[]} input.offlineAdministrations = Array of administration IDs that user will need access to offline
*/
constructor({ db, roarUid, assessmentUid, assessmentPid, userType = interfaces_1.UserType.guest, userMetadata = {}, testData = false, demoData = false, offlineEnabled = false, offlineTasks = [], offlineAdministrations = [], }) {
const allowedUserCategories = Object.values(interfaces_1.UserType);
if (!allowedUserCategories.includes(userType)) {
throw new Error(`User category must be one of ${allowedUserCategories.join(', ')}.`);
}
if (roarUid === undefined && userType !== interfaces_1.UserType.guest) {
throw new Error('All non-guest LEVANTE users must be created with a LEVANTE UID.');
}
if (userType === interfaces_1.UserType.guest && roarUid !== undefined) {
throw new Error('Guest LEVANTE users cannot have a LEVANTE UID.');
}
if (userType !== interfaces_1.UserType.guest && assessmentPid === undefined) {
throw new Error('All non-guest LEVANTE users must have an assessment PID on instantiation.');
}
this.db = db;
this.roarUid = roarUid;
this.assessmentPid = assessmentPid;
this.assessmentUid = assessmentUid;
this.userType = userType;
this.userMetadata = userMetadata;
this.testData = testData;
this.demoData = demoData;
this.offlineEnabled = offlineEnabled;
this.offlineTasks = offlineTasks;
this.offlineAdministrations = offlineAdministrations;
if (userType === interfaces_1.UserType.guest) {
this.userRef = (0, firestore_1.doc)(this.db, 'guests', this.assessmentUid);
}
else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.userRef = (0, firestore_1.doc)(this.db, 'users', this.roarUid);
}
}
async init() {
const docSnap = await (0, firestore_1.getDoc)(this.userRef);
this.onFirestore = docSnap.exists();
if (this.onFirestore) {
// If so, retrieve their data.
this.userData = docSnap.data();
}
else {
// Otherwise allow them to create their own data ONLY if they are a guest.
await this._setUserData();
}
}
async _setUserData() {
if (this.userType !== interfaces_1.UserType.guest) {
throw new Error('Cannot set user data on a non-guest LEVANTE user.');
}
this.userData = (0, util_1.removeUndefined)({
...this.userMetadata,
assessmentPid: this.assessmentPid,
assessmentUid: this.assessmentUid,
userType: this.userType,
});
await (0, firestore_1.setDoc)(this.userRef, {
...this.userData,
created: (0, firestore_1.serverTimestamp)(),
createdAt: (0, firestore_1.serverTimestamp)(),
updatedAt: (0, firestore_1.serverTimestamp)(),
});
this.onFirestore = true;
}
async checkUserExists() {
if (!this.onFirestore) {
await this.init();
}
if (!this.onFirestore) {
throw new Error('This non-guest user is not in Firestore.');
}
}
/**
* Update the user's data (both locally and in Firestore)
* @param {object} input
* @param {string[]} input.tasks - The tasks to be added to the user doc
* @param {string[]} input.variants - The variants to be added to the user doc
* @param {string} input.assessmentPid - The assessment PID of the user
* @param {*} input.userMetadata - Any additional user metadata
* @method
* @async
*/
async updateUser({ tasks, variants, assessmentPid, ...userMetadata }) {
this.checkUserExists();
let userData = {
lastUpdated: (0, firestore_1.serverTimestamp)(),
};
if (tasks)
userData.tasks = (0, firestore_1.arrayUnion)(...tasks);
if (variants)
userData.variants = (0, firestore_1.arrayUnion)(...variants);
if (this.userType === interfaces_1.UserType.guest) {
if (assessmentPid)
userData.assessmentPid = assessmentPid;
userData = {
...userMetadata,
...userData,
};
}
this.userData = (0, extend_1.default)(this.userData, {
...userMetadata,
tasks,
variants,
assessmentPid,
});
return await (0, firestore_1.updateDoc)(this.userRef, (0, util_1.removeUndefined)(userData));
}
}
exports.RoarAppUser = RoarAppUser;