mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-11 19:08:20 -05:00
feat: adds data migration script WIP
This commit is contained in:
@@ -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 = (
|
||||
|
||||
@@ -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();
|
||||
Reference in New Issue
Block a user