UNPKG

@levante-framework/firekit

Version:

A library to facilitate Firebase authentication and Firestore interaction for LEVANTE apps

147 lines (146 loc) 7.28 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RoarTaskVariant = void 0; const firestore_1 = require("firebase/firestore"); const isEqual_1 = __importDefault(require("lodash/isEqual")); const util_1 = require("../util"); /** * Class representing a ROAR task. */ class RoarTaskVariant { /** Create a ROAR task * @param {TaskVariantInput} input * @param {Firestore} input.db - The assessment Firestore instance to which this task'data will be written * @param {string} input.taskId - The ID of the parent task. Should be a short initialism, e.g. "swr" or "sre" * @param {string} input.taskName - The name of the parent task * @param {string} input.taskDescription - The description of the task * @param {string} input.variantName - The name of the task variant * @param {string} input.variantDescription - The description of the variant * @param {object} input.variantParams - The parameters of the task variant */ constructor({ db, taskId, taskName, taskDescription, taskImage, taskURL, gameConfig, taskVersion = undefined, registered, external, variantId = undefined, variantName, variantParams = {}, }) { this.db = db; this.taskId = taskId.toLowerCase(); this.taskName = taskName; this.taskDescription = taskDescription; this.taskImage = taskImage; this.taskURL = taskURL; this.taskVersion = taskVersion; this.gameConfig = gameConfig; this.registered = registered; this.external = external; this.variantName = variantName; this.variantParams = variantParams; this.taskRef = (0, firestore_1.doc)(this.db, 'tasks', this.taskId); this.variantsCollectionRef = (0, firestore_1.collection)(this.taskRef, 'variants'); this.variantId = variantId; this.variantRef = variantId ? (0, firestore_1.doc)(this.variantsCollectionRef, variantId) : undefined; } /** * Push the trial and trial variant to Firestore * @method * @async */ async toFirestore() { // Check if task document exists to determine if we should set createdAt const taskDocSnap = await (0, firestore_1.getDoc)(this.taskRef); const taskExists = taskDocSnap.exists(); // Push/update the task using the user provided task ID const taskData = { name: this.taskName, description: this.taskDescription, image: this.taskImage, taskURL: this.taskURL, gameConfig: this.gameConfig, registered: this.registered, external: this.external, lastUpdated: (0, firestore_1.serverTimestamp)(), updatedAt: (0, firestore_1.serverTimestamp)(), // Only set createdAt if this is a new document ...(!taskExists && { createdAt: (0, firestore_1.serverTimestamp)() }), }; try { await (0, firestore_1.setDoc)(this.taskRef, (0, util_1.removeUndefined)(taskData), { merge: true }); } catch (error) { console.error('RoarTaskVariant toFirestore: error saving task to firestore', { error, errorMessage: error instanceof Error ? error.message : String(error), errorCode: error?.code, taskId: this.taskId, taskRefPath: this.taskRef?.path, }); throw error; } // Check to see if variant exists already by querying for a match on the params. const q = (0, firestore_1.query)(this.variantsCollectionRef, (0, firestore_1.where)('params', '==', this.variantParams), (0, firestore_1.orderBy)('updatedAt', 'desc'), (0, firestore_1.limit)(1)); const querySnapshot = await (0, firestore_1.getDocs)(q); let foundVariantWithCurrentParams = false; // If this query snapshot yielded results, then we can use it and // update the timestamp querySnapshot.forEach((docRef) => { this.variantId = docRef.id; this.variantRef = (0, firestore_1.doc)(this.variantsCollectionRef, this.variantId); foundVariantWithCurrentParams = true; (0, firestore_1.updateDoc)(this.variantRef, (0, util_1.removeUndefined)({ lastUpdated: (0, firestore_1.serverTimestamp)(), updatedAt: (0, firestore_1.serverTimestamp)(), })); }); if (!foundVariantWithCurrentParams) { // Create new variant with both createdAt and updatedAt const variantData = { name: this.variantName, taskURL: this.taskURL, registered: this.registered, external: this.external, params: this.variantParams, lastUpdated: (0, firestore_1.serverTimestamp)(), createdAt: (0, firestore_1.serverTimestamp)(), updatedAt: (0, firestore_1.serverTimestamp)(), }; this.variantRef = (0, firestore_1.doc)(this.variantsCollectionRef); await (0, firestore_1.setDoc)(this.variantRef, (0, util_1.removeUndefined)(variantData)); this.variantId = this.variantRef.id; } } /** * Update variant params in Firestore * @method * @param {object} newParams - The parameters of the task variant * @async */ async updateTaskParams(newParams) { if (this.variantRef === undefined) { throw new Error('Cannot update task params before writing task to Firestore. Please call `.toFirestore()` first.'); } const oldParams = (0, util_1.replaceValues)(this.variantParams); const cleanParams = (0, util_1.replaceValues)(newParams); // Only allow updating the task params if we are updating previously null values. const { merged } = (0, util_1.mergeGameParams)(oldParams, cleanParams); this.variantParams = merged; await this.toFirestore(); } async setVariantRef() { // If this is being called that means the variantId was not provided in the constructor if (!this.variantParams) { // If variant params are not then there is no way to determine the variant ref throw new Error('Cannot set variant ref without variant params or variant id. Please provide one of them.'); } // Query all the variants and do a deep match on the params field const q = (0, firestore_1.query)(this.variantsCollectionRef, (0, firestore_1.orderBy)('updatedAt', 'desc')); const querySnapshot = await (0, firestore_1.getDocs)(q); // Go through the docs and match the this.variantParams with the params field in the doc for (const docSnap of querySnapshot.docs) { if ((0, isEqual_1.default)(docSnap.data().params, this.variantParams)) { this.variantId = docSnap.id; break; } } this.variantRef = (0, firestore_1.doc)(this.variantsCollectionRef, this.variantId); } } exports.RoarTaskVariant = RoarTaskVariant;