From ac0491b54aa56dac7783eed1163deff4606a70d2 Mon Sep 17 00:00:00 2001 From: YUUU23 Date: Thu, 1 Aug 2024 13:12:45 -0500 Subject: [PATCH 1/9] add back register --- cli.mjs | 160 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 140 insertions(+), 20 deletions(-) diff --git a/cli.mjs b/cli.mjs index 2d6b6f41..a17d512d 100644 --- a/cli.mjs +++ b/cli.mjs @@ -47,12 +47,25 @@ commander STUDY_ID = studyID; PARTICIPANT_ID = participantID; }); + +// register: optional argument studyID and participantID skips relative prompts +commander + .command(`register`) + .argument(`[studyID]`) + .argument(`[participantID]`) + .description(`Register new partipant under study provided a partipantID and studyID`) + .action((studyID, participantID) => { + ACTION = "register"; + STUDY_ID = studyID; + PARTICIPANT_ID = participantID; + }); + commander.parse(); // print message if download or delete provided, along with optional args provided if (ACTION != undefined) { console.log( - `${ACTION} data from Firebase given ${STUDY_ID === undefined ? "" : `study ID: ${STUDY_ID}`} ${PARTICIPANT_ID === undefined ? "" : `and participant ID: ${PARTICIPANT_ID}`}` + `${ACTION} data from Firebase ${STUDY_ID == undefined ? "" : `given study ID: ${STUDY_ID}`} ${PARTICIPANT_ID == undefined ? "" : `and participant ID: ${PARTICIPANT_ID}`}` ); } @@ -70,8 +83,8 @@ async function main() { } else { // when args directly passed in through CLI, check if study is valid const hasStudy = await validateStudyFirebase(STUDY_ID); - if (!hasStudy) { - console.error("Please enter a valid study from your Firestore database"); + if (hasStudy != true && ACTION != "register") { + console.error(hasStudy); return; } } @@ -80,9 +93,9 @@ async function main() { PARTICIPANT_ID = await participantIDPrompt(); } else { // when args directly passed in through CLI, check if participant is valid - const hasParticipant = await validateParticipantFirebase(PARTICIPANT_ID); - if (!hasParticipant) { - console.error(`Please enter a valid participant on the study "${STUDY_ID}"`); + const hasParticipant = await validateParticipantFirebase(STUDY_ID); + if (hasParticipant != true && ACTION != "register") { + console.error(hasParticipant); return; } } @@ -108,6 +121,15 @@ async function main() { throw INVALID_DEPLOYMENT_ERROR; } break; + case "register": + switch (DEPLOYMENT) { + case "firebase": + await registerDataFirebase(STUDY_ID, PARTICIPANT_ID); + break; + default: + throw INVALID_DEPLOYMENT_ERROR; + } + break; default: throw INVALID_ACTION_ERROR; } @@ -198,6 +220,24 @@ async function deleteDataFirebase() { } else console.log("Skipping deletion"); } +/** -------------------- REGISTER ACTION -------------------- */ + +/** Register new data, write to Firestore */ +async function registerDataFirebase(studyID, participantID) { + const confirmation = await confirmRegisterPrompt(studyID, participantID); + if (confirmation) { + try { + await addStudyAndParticipant(studyID, participantID); + } catch (error) { + console.error( + `Unable to register new participant with participantID ${participantID} under studyID ${studyID}: ` + + error + ); + } + } else console.log("Skipping registration"); + return true; +} + /** -------------------- PROMPTS -------------------- */ /** Prompt the user for the action they are trying to complete */ @@ -209,6 +249,10 @@ async function actionPrompt() { name: "Download data", value: "download", }, + { + name: "Register new participant under study", + value: "register", + }, { name: "Delete data", value: "delete", @@ -243,22 +287,25 @@ async function deploymentPrompt() { /** Prompt the user to enter the ID of a study */ // helper to check if the given study (input) is in firestore -async function validateStudyFirebase(input) { +const validateStudyFirebase = async (input) => { + const invalidMessage = "Please enter a valid study from your Firestore database"; // subcollection is programmatically generated, if it doesn't exist then input must not be a valid studyID const studyIDCollections = await getStudyRef(input).listCollections(); - return studyIDCollections.find((c) => c.id === PARTICIPANTS_COL); -} + return studyIDCollections.find((c) => c.id === PARTICIPANTS_COL) ? true : invalidMessage; +}; async function studyIDPrompt() { - const invalidMessage = "Please enter a valid study from your Firestore database"; return await input({ message: "Select a study:", validate: async (input) => { if (!input) return invalidMessage; + if (ACTION == "register") { + STUDY_ID = input; + return true; + } switch (DEPLOYMENT) { case "firebase": - const res = await validateStudyFirebase(input); - return !res ? invalidMessage : true; + return validateStudyFirebase(input); default: throw INVALID_DEPLOYMENT_ERROR; } @@ -268,25 +315,27 @@ async function studyIDPrompt() { /** Prompt the user to enter the ID of a participant on the STUDY_ID study */ // helper to check if the given participant (input) is in firestore under study -async function validateParticipantFirebase(input) { +const validateParticipantFirebase = async (input) => { + const invalidMessage = `Please enter a valid participant on the study "${STUDY_ID}"`; // subcollection is programmatically generated, if it doesn't exist then input must not be a valid participantID const studyIDCollections = await getParticipantRef(STUDY_ID, input).listCollections(); - return studyIDCollections.find((c) => c.id === DATA_COL); -} + return studyIDCollections.find((c) => c.id === DATA_COL) ? true : invalidMessage; +}; async function participantIDPrompt() { - const invalidMessage = `Please enter a valid participant on the study "${STUDY_ID}"`; return await input({ - message: "Select a participant:", + message: ACTION == "register" ? "Enter a new participant:" : "Select a participant:", validate: async (input) => { const invalid = "Please enter a valid participant from your Firestore database"; if (!input) return invalid; else if (input === "*") return true; - + if (ACTION == "register") { + PARTICIPANT_ID = input; + return true; + } switch (DEPLOYMENT) { case "firebase": - const res = await validateParticipantFirebase(input); - return !res ? invalidMessage : true; + return validateParticipantFirebase(input); default: throw INVALID_DEPLOYMENT_ERROR; } @@ -296,6 +345,11 @@ async function participantIDPrompt() { /** Prompt the user to select one or more experiments of the PARTICIPANT_ID on STUDY_ID */ async function experimentIDPrompt() { + // register: adding/checking for existing new studies will be done in function + if (ACTION == "register") { + return; + } + const dataSnapshot = await getDataRef(STUDY_ID, PARTICIPANT_ID).get(); // Sort experiment choices by most recent first @@ -339,6 +393,20 @@ async function confirmDeletionPrompt() { }); } +async function confirmRegisterPrompt(studyID, participantID) { + const currentParticipants = await getRegisteredParticipantArr(studyID); + const currentParticipantMessage = + currentParticipants.length == 0 + ? "Currently, there are no participants under this study\n" + : `Currently, the participants under this study include: \n${currentParticipants.join("\n")}\n`; + return confirm({ + message: + currentParticipantMessage + + `Continue? adding study with studyID: ${studyID} and participant ID: ${participantID}`, + default: false, + }); +} + /** * Prompts the user to confirm continuation of the CLI, including future conflicts * @param {string} outputFile @@ -376,6 +444,7 @@ async function confirmOverwritePrompt(file, overwriteAll) { /** -------------------- FIRESTORE HELPERS -------------------- */ const RESPONSES_COL = "participant_responses"; +const REG_STUDY_COL = "registered_studies"; const PARTICIPANTS_COL = "participants"; const DATA_COL = "data"; const TRIALS_COL = "trials"; @@ -394,3 +463,54 @@ const getDataRef = (studyID, participantID) => // Get a reference to a participant's specific experiment data document in Firestore const getExperimentRef = (studyID, participantID, experimentID) => getDataRef(studyID, participantID).doc(experimentID); + +// Get a reference to a registered study +const getRegisteredfStudyRef = (studyID) => FIRESTORE.collection(REG_STUDY_COL).doc(studyID); + +// Get current registered participant array under the StudyID +const getRegisteredParticipantArr = (studyID) => { + const res = getRegisteredfStudyRef(studyID) + .get() + .then((data) => { + if (data["_fieldsProto"] != undefined) { + return data["_fieldsProto"]["registered_participants"]["arrayValue"]["values"] + .filter((item) => item.valueType === "stringValue") + .map((item) => item.stringValue); + } else { + return []; + } + }); + + return res; +}; + +// Register new participantID under the provided studyID +const registerNewParticipant = async (studyID, participantID) => { + const currParticipants = await getRegisteredParticipantArr(studyID); + const newParticipantArray = [...currParticipants, participantID]; + const newData = { registered_participants: newParticipantArray }; + getRegisteredfStudyRef(studyID).update(newData); + console.log( + `Successfully added study and participant. Current participantIDs under study ${studyID}: \n${newParticipantArray.join("\n")}` + ); +}; + +// Add new participantID under studyID to Firestore under registered_studies, +// creates new study if studyID doesn't exist +const addStudyAndParticipant = async (studyID, participantID) => { + getRegisteredfStudyRef(studyID) + .get() + .then((data) => { + // study not initated yet + if (data["_fieldsProto"] == undefined) { + getRegisteredfStudyRef(studyID) + .set({ registered_participants: [] }) + .then(() => { + registerNewParticipant(studyID, participantID); + }); + } else { + // study initiated, add to array holding registered participants + registerNewParticipant(studyID, participantID); + } + }); +}; From 715766fa5ba6aebdcd84c579f8442c157ea8ad1d Mon Sep 17 00:00:00 2001 From: YUUU23 Date: Thu, 1 Aug 2024 13:37:59 -0500 Subject: [PATCH 2/9] ref: description, strictly equal --- cli.mjs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/cli.mjs b/cli.mjs index a17d512d..65113e15 100644 --- a/cli.mjs +++ b/cli.mjs @@ -21,7 +21,7 @@ const INVALID_DEPLOYMENT_ERROR = new Error("Invalid deployment: " + DEPLOYMENT); /** -------------------- COMMANDER -------------------- */ const commander = new Command(); -// default: [download | delete ] not provided, run main() as usual continuing with prompting +// default: [download | delete | register ] not provided, run main() as usual continuing with prompting commander.action(() => {}); // download: optional argument studyID and participantID skips relative prompts @@ -53,7 +53,9 @@ commander .command(`register`) .argument(`[studyID]`) .argument(`[participantID]`) - .description(`Register new partipant under study provided a partipantID and studyID`) + .description( + `Register new partipant under study provided a partipantID and studyID; new study will be created if not found` + ) .action((studyID, participantID) => { ACTION = "register"; STUDY_ID = studyID; @@ -62,23 +64,23 @@ commander commander.parse(); -// print message if download or delete provided, along with optional args provided -if (ACTION != undefined) { - console.log( - `${ACTION} data from Firebase ${STUDY_ID == undefined ? "" : `given study ID: ${STUDY_ID}`} ${PARTICIPANT_ID == undefined ? "" : `and participant ID: ${PARTICIPANT_ID}`}` - ); -} - /** -------------------- MAIN -------------------- */ // TODO @brown-ccv #289: Pass CLI arguments with commander (especially for action) async function main() { - if (ACTION == undefined) { + // print message if download or delete provided, along with optional args provided + if (ACTION != undefined) { + console.log( + `${ACTION} data from Firebase ${STUDY_ID === undefined ? "" : `given study ID: ${STUDY_ID}`} ${PARTICIPANT_ID === undefined ? "" : `and participant ID: ${PARTICIPANT_ID}`}` + ); + } + + if (ACTION === undefined) { ACTION = await actionPrompt(); } DEPLOYMENT = await deploymentPrompt(); // TODO @brown-ccv #291: Enable downloading all study data at once - if (STUDY_ID == undefined) { + if (STUDY_ID === undefined) { STUDY_ID = await studyIDPrompt(); } else { // when args directly passed in through CLI, check if study is valid @@ -89,7 +91,7 @@ async function main() { } } // TODO @brown-ccv #291: Enable downloading all participant data at once - if (PARTICIPANT_ID == undefined) { + if (PARTICIPANT_ID === undefined) { PARTICIPANT_ID = await participantIDPrompt(); } else { // when args directly passed in through CLI, check if participant is valid @@ -299,7 +301,7 @@ async function studyIDPrompt() { message: "Select a study:", validate: async (input) => { if (!input) return invalidMessage; - if (ACTION == "register") { + if (ACTION === "register") { STUDY_ID = input; return true; } @@ -324,12 +326,12 @@ const validateParticipantFirebase = async (input) => { async function participantIDPrompt() { return await input({ - message: ACTION == "register" ? "Enter a new participant:" : "Select a participant:", + message: ACTION === "register" ? "Enter a new participant:" : "Select a participant:", validate: async (input) => { const invalid = "Please enter a valid participant from your Firestore database"; if (!input) return invalid; else if (input === "*") return true; - if (ACTION == "register") { + if (ACTION === "register") { PARTICIPANT_ID = input; return true; } @@ -346,7 +348,7 @@ async function participantIDPrompt() { /** Prompt the user to select one or more experiments of the PARTICIPANT_ID on STUDY_ID */ async function experimentIDPrompt() { // register: adding/checking for existing new studies will be done in function - if (ACTION == "register") { + if (ACTION === "register") { return; } From 62dbe641d27fc4073601474ffe744bdd9916f2ee Mon Sep 17 00:00:00 2001 From: YUUU23 Date: Sun, 4 Aug 2024 12:24:00 -0500 Subject: [PATCH 3/9] ensure everything is strictly equal in cli script --- cli.mjs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli.mjs b/cli.mjs index 65113e15..20358eba 100644 --- a/cli.mjs +++ b/cli.mjs @@ -85,7 +85,7 @@ async function main() { } else { // when args directly passed in through CLI, check if study is valid const hasStudy = await validateStudyFirebase(STUDY_ID); - if (hasStudy != true && ACTION != "register") { + if (hasStudy !== true && ACTION !== "register") { console.error(hasStudy); return; } @@ -96,7 +96,7 @@ async function main() { } else { // when args directly passed in through CLI, check if participant is valid const hasParticipant = await validateParticipantFirebase(STUDY_ID); - if (hasParticipant != true && ACTION != "register") { + if (hasParticipant !== true && ACTION !== "register") { console.error(hasParticipant); return; } @@ -398,7 +398,7 @@ async function confirmDeletionPrompt() { async function confirmRegisterPrompt(studyID, participantID) { const currentParticipants = await getRegisteredParticipantArr(studyID); const currentParticipantMessage = - currentParticipants.length == 0 + currentParticipants.length === 0 ? "Currently, there are no participants under this study\n" : `Currently, the participants under this study include: \n${currentParticipants.join("\n")}\n`; return confirm({ @@ -504,7 +504,7 @@ const addStudyAndParticipant = async (studyID, participantID) => { .get() .then((data) => { // study not initated yet - if (data["_fieldsProto"] == undefined) { + if (data["_fieldsProto"] === undefined) { getRegisteredfStudyRef(studyID) .set({ registered_participants: [] }) .then(() => { From 998e9173f6c22a84f1fbd54d5107f5e6645ecea9 Mon Sep 17 00:00:00 2001 From: Megan <113795130+YUUU23@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:14:42 -0500 Subject: [PATCH 4/9] Update cli.mjs add commander.parse() to main function Co-authored-by: Robert Gemma <38439940+RobertGemmaJr@users.noreply.github.com> --- cli.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli.mjs b/cli.mjs index 20358eba..ba7e7e3e 100644 --- a/cli.mjs +++ b/cli.mjs @@ -62,12 +62,11 @@ commander PARTICIPANT_ID = participantID; }); -commander.parse(); /** -------------------- MAIN -------------------- */ -// TODO @brown-ccv #289: Pass CLI arguments with commander (especially for action) async function main() { +commander.parse(); // print message if download or delete provided, along with optional args provided if (ACTION != undefined) { console.log( From 8bfce2436fc4a554b0a5de17bb125ecf832d9e30 Mon Sep 17 00:00:00 2001 From: YUUU23 Date: Wed, 14 Aug 2024 14:37:17 -0500 Subject: [PATCH 5/9] ref: async functions, remove duplicate helpers --- cli.mjs | 64 +++++++++++++++------------------------------------------ 1 file changed, 17 insertions(+), 47 deletions(-) diff --git a/cli.mjs b/cli.mjs index 6b35918d..3a21430f 100644 --- a/cli.mjs +++ b/cli.mjs @@ -285,15 +285,6 @@ async function deploymentPrompt() { return response; } -/** Prompt the user to enter the ID of a study */ -// helper to check if the given study (input) is in firestore -const validateStudyFirebase = async (input) => { - const invalidMessage = "Please enter a valid study from your Firestore database"; - // subcollection is programmatically generated, if it doesn't exist then input must not be a valid studyID - const studyIDCollections = await getStudyRef(input).listCollections(); - return studyIDCollections.find((c) => c.id === PARTICIPANTS_COL) ? true : invalidMessage; -}; - async function studyIDPrompt() { return await input({ message: "Select a study:", @@ -314,15 +305,6 @@ async function studyIDPrompt() { }); } -/** Prompt the user to enter the ID of a participant on the STUDY_ID study */ -// helper to check if the given participant (input) is in firestore under study -const validateParticipantFirebase = async (input) => { - const invalidMessage = `Please enter a valid participant on the study "${STUDY_ID}"`; - // subcollection is programmatically generated, if it doesn't exist then input must not be a valid participantID - const studyIDCollections = await getParticipantRef(STUDY_ID, input).listCollections(); - return studyIDCollections.find((c) => c.id === DATA_COL) ? true : invalidMessage; -}; - async function participantIDPrompt() { return await input({ message: ACTION === "register" ? "Enter a new participant:" : "Select a participant:", @@ -485,20 +467,17 @@ const getExperimentRef = (studyID, participantID, experimentID) => const getRegisteredfStudyRef = (studyID) => FIRESTORE.collection(REG_STUDY_COL).doc(studyID); // Get current registered participant array under the StudyID -const getRegisteredParticipantArr = (studyID) => { - const res = getRegisteredfStudyRef(studyID) - .get() - .then((data) => { - if (data["_fieldsProto"] != undefined) { - return data["_fieldsProto"]["registered_participants"]["arrayValue"]["values"] - .filter((item) => item.valueType === "stringValue") - .map((item) => item.stringValue); - } else { - return []; - } - }); - - return res; +const getRegisteredParticipantArr = async (studyID) => { + const data = await getRegisteredfStudyRef(studyID).get(); + if (data["_fieldsProto"] !== undefined) { + // get array of registered participant under study + return data["_fieldsProto"]["registered_participants"]["arrayValue"]["values"] + .filter((item) => item.valueType === "stringValue") + .map((item) => item.stringValue); + } else { + // return empty array when no participant found + return []; + } }; // Register new participantID under the provided studyID @@ -515,19 +494,10 @@ const registerNewParticipant = async (studyID, participantID) => { // Add new participantID under studyID to Firestore under registered_studies, // creates new study if studyID doesn't exist const addStudyAndParticipant = async (studyID, participantID) => { - getRegisteredfStudyRef(studyID) - .get() - .then((data) => { - // study not initated yet - if (data["_fieldsProto"] === undefined) { - getRegisteredfStudyRef(studyID) - .set({ registered_participants: [] }) - .then(() => { - registerNewParticipant(studyID, participantID); - }); - } else { - // study initiated, add to array holding registered participants - registerNewParticipant(studyID, participantID); - } - }); + const data = await getRegisteredfStudyRef(studyID).get(); + if (data["_fieldsProto"] === undefined) { + // study not initiated yet + await getRegisteredfStudyRef(studyID).set({ registered_participants: [] }); + } + await registerNewParticipant(studyID, participantID); }; From a26eedbb90f3bdee04a6ca5e04388b1de02b8a50 Mon Sep 17 00:00:00 2001 From: YUUU23 Date: Wed, 14 Aug 2024 16:54:35 -0500 Subject: [PATCH 6/9] fix: logical expression to change back to casting to bool for prompt functions --- cli.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli.mjs b/cli.mjs index b30b7ff8..dca4d1fc 100644 --- a/cli.mjs +++ b/cli.mjs @@ -251,7 +251,7 @@ async function studyIDPrompt() { switch (DEPLOYMENT) { case "firebase": const studyCollection = await validateStudyFirebase(input); - return studyCollection || invalidMessage; + return !studyCollection ? invalidMessage : true; default: throw INVALID_DEPLOYMENT_ERROR; } @@ -272,7 +272,7 @@ async function participantIDPrompt() { switch (DEPLOYMENT) { case "firebase": const participantCollection = await validateParticipantFirebase(input); - return participantCollection || invalidMessage; + return !participantCollection ? invalidMessage : true; default: throw INVALID_DEPLOYMENT_ERROR; } From 076038d40365979fcbf6fa0b6a77a97f5f3a5916 Mon Sep 17 00:00:00 2001 From: YUUU23 Date: Wed, 14 Aug 2024 17:36:58 -0500 Subject: [PATCH 7/9] add: add back commander and util functions --- cli.mjs | 16 ++++++++++++++++ package-lock.json | 39 ++++++++++++++++----------------------- package.json | 1 - 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/cli.mjs b/cli.mjs index 0b31f15a..08a27a43 100644 --- a/cli.mjs +++ b/cli.mjs @@ -3,6 +3,7 @@ import fsExtra from "fs-extra"; import { cert, initializeApp } from "firebase-admin/app"; // eslint-disable-line import/no-unresolved import { getFirestore } from "firebase-admin/firestore"; // eslint-disable-line import/no-unresolved +import { Command } from "commander"; /** -------------------- GLOBALS -------------------- */ @@ -422,6 +423,21 @@ async function confirmOverwritePrompt(file, overwriteAll) { return answer; } +/** -------------------- FIRESTORE VALIDATIONS -------------------- */ +/** helper to check if the given study (input) is in firestore */ +async function validateStudyFirebase(input) { + // subcollection is programmatically generated, if it doesn't exist then input must not be a valid studyID + const studyIDCollections = await getStudyRef(input).listCollections(); + return studyIDCollections.find((c) => c.id === PARTICIPANTS_COL); +} + +/** helper to check if the given participant (input) is in firestore under study */ +async function validateParticipantFirebase(input) { + // subcollection is programmatically generated, if it doesn't exist then input must not be a valid participantID + const studyIDCollections = await getParticipantRef(STUDY_ID, input).listCollections(); + return studyIDCollections.find((c) => c.id === DATA_COL); +} + /** -------------------- FIRESTORE HELPERS -------------------- */ const RESPONSES_COL = "participant_responses"; diff --git a/package-lock.json b/package-lock.json index 47f00e60..1981eae6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,6 @@ "@jspsych/plugin-instructions": "^1.1.3", "@jspsych/plugin-preload": "^1.1.2", "@jspsych/plugin-survey": "^1.0.1", - "commander": "^12.1.0", "electron-log": "^5.0.0", "electron-squirrel-startup": "^1.0.0", "firebase": "^10.11.0", @@ -656,15 +655,6 @@ "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/cli/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/@electron-forge/cli/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -6316,11 +6306,13 @@ } }, "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 6" } }, "node_modules/commitizen": { @@ -9646,15 +9638,6 @@ "node": ">= 10" } }, - "node_modules/firebase-tools/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/firebase-tools/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -12556,6 +12539,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lint-staged/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/lint-staged/node_modules/emoji-regex": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", diff --git a/package.json b/package.json index 7f79f107..83ea4663 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ "@jspsych/plugin-instructions": "^1.1.3", "@jspsych/plugin-preload": "^1.1.2", "@jspsych/plugin-survey": "^1.0.1", - "commander": "^12.1.0", "electron-log": "^5.0.0", "electron-squirrel-startup": "^1.0.0", "firebase": "^10.11.0", From 9b947b1336ff561c079bab6c8ce8d56baff79a91 Mon Sep 17 00:00:00 2001 From: YUUU23 Date: Wed, 14 Aug 2024 17:43:54 -0500 Subject: [PATCH 8/9] add: add back validation functions and dependencies --- cli.mjs | 21 ++++++++++----------- package-lock.json | 39 +++++++++++++++++++++++---------------- package.json | 1 + 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/cli.mjs b/cli.mjs index 08a27a43..87a8d751 100644 --- a/cli.mjs +++ b/cli.mjs @@ -285,18 +285,17 @@ async function deploymentPrompt() { return response; } +/** Prompt the user to enter the ID of a study */ async function studyIDPrompt() { + const invalidMessage = "Please enter a valid study from your Firestore database"; return await input({ message: "Select a study:", validate: async (input) => { if (!input) return invalidMessage; - if (ACTION === "register") { - STUDY_ID = input; - return true; - } switch (DEPLOYMENT) { case "firebase": - return validateStudyFirebase(input); + const studyCollection = await validateStudyFirebase(input); + return !studyCollection ? invalidMessage : true; default: throw INVALID_DEPLOYMENT_ERROR; } @@ -304,20 +303,20 @@ async function studyIDPrompt() { }); } +/** Prompt the user to enter the ID of a participant on the STUDY_ID study */ async function participantIDPrompt() { + const invalidMessage = `Please enter a valid participant on the study "${STUDY_ID}"`; return await input({ - message: ACTION === "register" ? "Enter a new participant:" : "Select a participant:", + message: "Select a participant:", validate: async (input) => { const invalid = "Please enter a valid participant from your Firestore database"; if (!input) return invalid; else if (input === "*") return true; - if (ACTION === "register") { - PARTICIPANT_ID = input; - return true; - } + switch (DEPLOYMENT) { case "firebase": - return validateParticipantFirebase(input); + const participantCollection = await validateParticipantFirebase(input); + return !participantCollection ? invalidMessage : true; default: throw INVALID_DEPLOYMENT_ERROR; } diff --git a/package-lock.json b/package-lock.json index 1981eae6..47f00e60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@jspsych/plugin-instructions": "^1.1.3", "@jspsych/plugin-preload": "^1.1.2", "@jspsych/plugin-survey": "^1.0.1", + "commander": "^12.1.0", "electron-log": "^5.0.0", "electron-squirrel-startup": "^1.0.0", "firebase": "^10.11.0", @@ -655,6 +656,15 @@ "node": ">= 16.4.0" } }, + "node_modules/@electron-forge/cli/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/@electron-forge/cli/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -6306,13 +6316,11 @@ } }, "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "license": "MIT", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "engines": { - "node": ">= 6" + "node": ">=18" } }, "node_modules/commitizen": { @@ -9638,6 +9646,15 @@ "node": ">= 10" } }, + "node_modules/firebase-tools/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/firebase-tools/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -12539,16 +12556,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/lint-staged/node_modules/emoji-regex": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", diff --git a/package.json b/package.json index 83ea4663..7f79f107 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@jspsych/plugin-instructions": "^1.1.3", "@jspsych/plugin-preload": "^1.1.2", "@jspsych/plugin-survey": "^1.0.1", + "commander": "^12.1.0", "electron-log": "^5.0.0", "electron-squirrel-startup": "^1.0.0", "firebase": "^10.11.0", From a358f210ad9c20ddd34eea00f70f115fd4e333aa Mon Sep 17 00:00:00 2001 From: YUUU23 Date: Wed, 14 Aug 2024 20:26:16 -0500 Subject: [PATCH 9/9] add: add back prompt checking --- cli.mjs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/cli.mjs b/cli.mjs index 87a8d751..a60227a2 100644 --- a/cli.mjs +++ b/cli.mjs @@ -1,8 +1,8 @@ import { checkbox, confirm, expand, input, select } from "@inquirer/prompts"; import fsExtra from "fs-extra"; -import { cert, initializeApp } from "firebase-admin/app"; // eslint-disable-line import/no-unresolved -import { getFirestore } from "firebase-admin/firestore"; // eslint-disable-line import/no-unresolved +import { cert, initializeApp } from "firebase-admin/app"; +import { getFirestore } from "firebase-admin/firestore"; import { Command } from "commander"; /** -------------------- GLOBALS -------------------- */ @@ -285,13 +285,16 @@ async function deploymentPrompt() { return response; } -/** Prompt the user to enter the ID of a study */ async function studyIDPrompt() { const invalidMessage = "Please enter a valid study from your Firestore database"; return await input({ message: "Select a study:", validate: async (input) => { if (!input) return invalidMessage; + if (ACTION === "register") { + STUDY_ID = input; + return true; + } switch (DEPLOYMENT) { case "firebase": const studyCollection = await validateStudyFirebase(input); @@ -303,16 +306,18 @@ async function studyIDPrompt() { }); } -/** Prompt the user to enter the ID of a participant on the STUDY_ID study */ async function participantIDPrompt() { const invalidMessage = `Please enter a valid participant on the study "${STUDY_ID}"`; return await input({ - message: "Select a participant:", + message: ACTION === "register" ? "Enter a new participant:" : "Select a participant:", validate: async (input) => { const invalid = "Please enter a valid participant from your Firestore database"; if (!input) return invalid; else if (input === "*") return true; - + if (ACTION === "register") { + PARTICIPANT_ID = input; + return true; + } switch (DEPLOYMENT) { case "firebase": const participantCollection = await validateParticipantFirebase(input);