feat: adds data migration script WIP

This commit is contained in:
Piyush Gupta
2024-08-28 17:55:34 +05:30
parent d56f05fb19
commit f1a2ecaa3a
2 changed files with 180 additions and 43 deletions

View File

@@ -240,7 +240,7 @@ export const getMatchValueProps = (
hiddenFields = hiddenFields.filter((field) => field !== condition.leftOperand.id);
}
let questionOptions = questions.map((question) => {
const questionOptions = questions.map((question) => {
return {
icon: questionIconMapping[question.type],
label: getLocalizedValue(question.headline, "default"),
@@ -251,7 +251,7 @@ export const getMatchValueProps = (
};
});
let variableOptions = variables.map((variable) => {
const variableOptions = variables.map((variable) => {
return {
icon: variable.type === "number" ? FileDigitIcon : FileType2Icon,
label: variable.name,
@@ -262,7 +262,7 @@ export const getMatchValueProps = (
};
});
let hiddenFieldsOptions = hiddenFields.map((field) => {
const hiddenFieldsOptions = hiddenFields.map((field) => {
return {
icon: EyeOffIcon,
label: field,
@@ -275,42 +275,6 @@ export const getMatchValueProps = (
const groupedOptions: ComboboxGroupedOption[] = [];
if (condition.leftOperand.type === "hiddenField") {
hiddenFieldsOptions = hiddenFieldsOptions?.filter((field) => field.value !== condition.leftOperand.id);
} else if (condition.leftOperand.type === "variable") {
variableOptions = variableOptions?.filter((variable) => variable.value !== condition.leftOperand.id);
} else if (condition.leftOperand.type === "question") {
questionOptions = questionOptions?.filter((question) => question.value !== condition.leftOperand.id);
const question = localSurvey.questions.find((question) => question.id === condition.leftOperand.id);
let choices: ComboboxOption[] = [];
if (
question &&
(question.type === TSurveyQuestionTypeEnum.MultipleChoiceSingle ||
question.type === TSurveyQuestionTypeEnum.MultipleChoiceMulti)
) {
choices = question.choices.map((choice) => ({
label: getLocalizedValue(choice.label, "default"),
value: choice.id,
}));
}
if (question && question.type === TSurveyQuestionTypeEnum.PictureSelection) {
choices = question.choices.map((choice, idx) => ({
label: choice.imageUrl.split("/").pop() || `Image ${idx + 1}`,
value: choice.id,
}));
}
if (choices.length > 0) {
groupedOptions.push({
label: "Choices",
value: "choices",
options: choices,
});
}
}
if (questionOptions.length > 0) {
groupedOptions.push({
label: "Questions",
@@ -336,10 +300,6 @@ export const getMatchValueProps = (
}
return { show: true, options: groupedOptions };
// const question = localSurvey.questions[questionIdx];
return { show: true, options: [] };
};
export const getActionTargetOptions = (

View File

@@ -0,0 +1,177 @@
/* eslint-disable no-console -- logging is allowed in migration scripts */
// !@gupta-piyush19: WIP
// Pending:
// 1. Options assignment: text to id
// 2. have to check and modify data storing in right operand based on the saving pattern of current logic
import { createId } from "@paralleldrive/cuid2";
import { PrismaClient } from "@prisma/client";
import type {
TAction,
TSingleCondition,
TSurveyAdvancedLogic,
TSurveyLogicCondition,
} from "@formbricks/types/surveys/logic";
import type { TSurveyQuestion } from "@formbricks/types/surveys/types";
const prisma = new PrismaClient();
interface TOldLogic {
condition: string;
value?: string | string[];
destination: string;
}
const doesRightOperandExist = (operator: TSurveyLogicCondition): boolean => {
return ![
"isAccepted",
"isBooked",
"isClicked",
"isCompletelySubmitted",
"isPartiallySubmitted",
"isSkipped",
"isSubmitted",
].includes(operator);
};
// Helper function to convert old logic condition to new format
function convertLogicCondition(
oldCondition: string,
oldValue: string | string[] | undefined,
questionId: string
): TSingleCondition {
const operator = mapOldConditionToNew(oldCondition);
const newCondition: TSingleCondition = {
id: createId(),
leftOperand: {
type: "question",
id: questionId,
},
operator,
...(doesRightOperandExist(operator) && {
rightOperand: {
type: "static",
value: oldValue ?? "",
},
}),
};
return newCondition;
}
// Helper function to map old conditions to new ones
function mapOldConditionToNew(oldCondition: string): TSurveyLogicCondition {
const conditionMap: Record<string, TSurveyLogicCondition> = {
accepted: "isAccepted",
clicked: "isClicked",
submitted: "isSubmitted",
skipped: "isSkipped",
equals: "equals",
notEquals: "doesNotEqual",
lessThan: "isLessThan",
lessEqual: "isLessThanOrEqual",
greaterThan: "isGreaterThan",
greaterEqual: "isGreaterThanOrEqual",
includesAll: "includesAllOf",
includesOne: "includesOneOf",
uploaded: "isSubmitted", // Assuming 'uploaded' maps to 'isSubmitted'
notUploaded: "isSkipped", // Assuming 'notUploaded' maps to 'isSkipped'
booked: "isBooked",
isCompletelySubmitted: "isCompletelySubmitted",
isPartiallySubmitted: "isPartiallySubmitted",
};
return conditionMap[oldCondition];
}
// Helper function to convert old logic to new format
function convertLogic(oldLogic: TOldLogic, questionId: string): TSurveyAdvancedLogic {
const condition: TSingleCondition = convertLogicCondition(oldLogic.condition, oldLogic.value, questionId);
condition.leftOperand.id = questionId;
const action: TAction = {
id: createId(),
objective: "jumpToQuestion",
target: oldLogic.destination,
};
return {
id: createId(),
conditions: {
id: createId(),
connector: null,
conditions: [condition],
},
actions: [action],
};
}
async function runMigration(): Promise<void> {
await prisma.$transaction(
async (tx) => {
const startTime = Date.now();
console.log("Starting survey logic migration...");
// Get all surveys with questions containing old logic
const relevantSurveys = await tx.survey.findMany({
where: {
questions: {
array_contains: [{ logic: { $exists: true } }],
},
},
select: {
id: true,
questions: true,
},
});
// Process each survey
const migrationPromises = relevantSurveys.map(async (survey) => {
const updatedQuestions = survey.questions.map((question: TSurveyQuestion) => {
if (question.logic && Array.isArray(question.logic)) {
const newLogic = (question.logic as TOldLogic[]).map((oldLogic) =>
convertLogic(oldLogic, question.id)
);
return { ...question, logic: newLogic };
}
return question;
});
return tx.survey.update({
where: { id: survey.id },
data: { questions: updatedQuestions },
});
});
await Promise.all(migrationPromises);
const endTime = Date.now();
console.log(
`Survey logic migration completed. Total time: ${((endTime - startTime) / 1000).toString()}s`
);
},
{
timeout: 300000, // 5 minutes
}
);
}
function handleError(error: unknown): void {
console.error("An error occurred during migration:", error);
process.exit(1);
}
function handleDisconnectError(): void {
console.error("Failed to disconnect Prisma client");
process.exit(1);
}
function main(): void {
runMigration()
.catch(handleError)
.finally(() => {
prisma.$disconnect().catch(handleDisconnectError);
});
}
main();