UNPKG

@bdelab/roar-firekit

Version:

A library to facilitate Firebase authentication and Cloud Firestore interaction for ROAR apps

163 lines (162 loc) 8.26 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; 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 ROAR users must be created with a ROAR UID.'); } if (userType === interfaces_1.UserType.guest && roarUid !== undefined) { throw new Error('Guest ROAR users cannot have a ROAR UID.'); } if (userType !== interfaces_1.UserType.guest && assessmentPid === undefined) { throw new Error('All non-guest ROAR 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); } } init() { return __awaiter(this, void 0, void 0, function* () { return (0, firestore_1.getDoc)(this.userRef).then((docSnap) => { 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. this._setUserData(); } }); }); } _setUserData() { return __awaiter(this, void 0, void 0, function* () { if (this.userType !== interfaces_1.UserType.guest) { throw new Error('Cannot set user data on a non-guest ROAR user.'); } this.userData = (0, util_1.removeUndefined)(Object.assign(Object.assign(Object.assign(Object.assign({}, this.userMetadata), { assessmentPid: this.assessmentPid, assessmentUid: this.assessmentUid, userType: this.userType }), (this.testData && { testData: true })), (this.demoData && { demoData: true }))); return (0, firestore_1.setDoc)(this.userRef, Object.assign(Object.assign({}, this.userData), { created: (0, firestore_1.serverTimestamp)() })).then(() => { this.onFirestore = true; }); }); } checkUserExists() { return __awaiter(this, void 0, void 0, function* () { if (!this.onFirestore) { yield 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 */ updateUser(_a) { var { tasks, variants, assessmentPid } = _a, userMetadata = __rest(_a, ["tasks", "variants", "assessmentPid"]); return __awaiter(this, void 0, void 0, function* () { 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 = Object.assign(Object.assign({}, userMetadata), userData); } this.userData = (0, extend_1.default)(this.userData, Object.assign(Object.assign({}, userMetadata), { tasks, variants, assessmentPid })); return (0, firestore_1.updateDoc)(this.userRef, (0, util_1.removeUndefined)(userData)); }); } /** * Update the user's "lastUpdated" timestamp * @method * @async */ updateFirestoreTimestamp() { return __awaiter(this, void 0, void 0, function* () { this.checkUserExists(); return (0, firestore_1.updateDoc)(this.userRef, { lastUpdated: (0, firestore_1.serverTimestamp)(), }); }); } } exports.RoarAppUser = RoarAppUser;