@levante-framework/firekit
Version:
A library to facilitate Firebase authentication and Firestore interaction for LEVANTE apps
147 lines (146 loc) • 7.28 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.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;