Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions migrations/1716472223-add-unique-id-to-questions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Form } from '@models';
import { startDatabaseForMigration } from '../src/utils/migrations/database.helper';
import { logger } from '@services/logger.service';
import { v4 as uuidv4 } from 'uuid';

/**
* Sample function of up migration
*/
export const up = async () => {
await startDatabaseForMigration();
// Update forms fields adding a new unique id for each field (question form)
const coreForms = await Form.find({ core: true }).select(
'fields name resource'
);

for (const coreForm of coreForms) {
// Start updating core forms
const coreFieldsToSave = [];
for (let field of coreForm.fields) {
field = {
...field,
oid: uuidv4(),
};
coreFieldsToSave.push(field);
}
coreForm.fields = coreFieldsToSave;
await coreForm.save();
logger.info(
`Form [${coreForm.name}]: updated fields with a unique id for each.`
);
// Update child forms (keep core fields with same id)
const childForms = await Form.find({
core: { $ne: true },
resource: coreForm.resource,
}).select('fields name resource');
if (childForms) {
for (const child of childForms) {
const childFieldsToSave = [];
for (let childField of child.fields) {
if (!childField.isCore) {
childField = {
...childField,
oid: uuidv4(),
};
} else {
const coreId = coreForm.fields.find(
(field) => field.name === childField.name
).oid;
childField = {
...childField,
oid: coreId,
};
}
childFieldsToSave.push(childField);
}
child.fields = childFieldsToSave;
await child.save();
logger.info(
`Form [${child.name}] (child form): updated fields with a unique id for each.`
);
}
}
}
};

/**
* Sample function of down migration
*
* @returns just migrate data.
*/
export const down = async () => {
/*
Code you downgrade script here!
*/
};
45 changes: 42 additions & 3 deletions src/schema/mutation/editForm.mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { logger } from '@services/logger.service';
import checkDefaultFields from '@utils/form/checkDefaultFields';
import { graphQLAuthCheck } from '@schema/shared';
import { Context } from '@server/apollo/context';
import { onUpdateFieldName } from '@utils/form/onUpdateFieldName';

/**
* List of keys of the structure's object which we want to inherit to the children forms when they are modified on the core form
Expand Down Expand Up @@ -332,6 +333,7 @@ export default {
// Update structure
const newStructure = JSON.parse(template.structure); // Get the inheriting form's structure
replaceField(
field.oid,
field.name,
newStructure,
structure,
Expand Down Expand Up @@ -426,7 +428,7 @@ export default {
if (!field.generated) {
// Remove from structure
const templateStructure = JSON.parse(template.structure);
removeField(templateStructure, field.name);
removeField(templateStructure, field.oid, field.name);
template.structure = JSON.stringify(templateStructure);
}
}
Expand All @@ -442,7 +444,12 @@ export default {
if (!field.generated) {
// Add to structure
const templateStructure = JSON.parse(template.structure);
addField(templateStructure, field.name, structure);
addField(
templateStructure,
field.oid,
field.name,
structure
);
template.structure = JSON.stringify(templateStructure);
}
}
Expand Down Expand Up @@ -525,10 +532,42 @@ export default {
update.$push = { versions: version._id };
}

const resForm = await Form.findByIdAndUpdate(args.id, update, {
let resForm = await Form.findByIdAndUpdate(args.id, update, {
new: true,
});

// Check if any field name was updated to also update records and aggregation/layouts
const updatedFieldName = await onUpdateFieldName(form, update.fields);
if (updatedFieldName) {
//Remove oldName property from form structure and fields
const structure = JSON.parse(update.structure);
const fields = update.fields.map((field: any) => {
if (field.oldName) {
delete field.oldName;
}
return field;
});
const pages = structure.pages.map((page: any) => {
page.elements = page.elements.map((element: any) => {
if (element.oldName) {
delete element.oldName;
}
return element;
});
return page;
});
structure.pages = pages;
resForm = await Form.findByIdAndUpdate(
args.id,
{
structure: JSON.stringify(structure),
fields: fields,
},
{
new: true,
}
);
}
// Return updated form
return resForm;
} catch (err) {
Expand Down
14 changes: 10 additions & 4 deletions src/utils/form/addField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@ import { getQuestionPosition } from './getQuestionPosition';
* Function by induction.
*
* @param structure structure of the form to edit
* @param oid unique id of the field to search for
* @param name name of the field to search for
* @param template structure of the core template
*/
export const addField = (structure: any, name: string, template: any): void => {
const templateQuestion = getQuestion(template, name);
export const addField = (
structure: any,
oid: string,
name: string,
template: any
): void => {
const templateQuestion = getQuestion(template, oid, name);
try {
const templatePreviousQuestion = getPreviousQuestion(template, name);
const templateNextQuestion = getNextQuestion(template, name);
const templatePreviousQuestion = getPreviousQuestion(template, oid, name);
const templateNextQuestion = getNextQuestion(template, oid);
if (templatePreviousQuestion) {
const { parent, index } = getQuestionPosition(
structure,
Expand Down
9 changes: 9 additions & 0 deletions src/utils/form/extractFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { GraphQLError } from 'graphql/error';
import { getFieldType } from './getFieldType';
import i18next from 'i18next';
import { validateGraphQLFieldName } from '@utils/validators';
import { v4 as uuidv4 } from 'uuid';

/**
* Push in fields array all detected fields in the json structure of object.
Expand Down Expand Up @@ -30,6 +31,7 @@ export const extractFields = async (object, fields, core): Promise<void> => {
const field = {
type,
name: element.valueName,
oid: uuidv4(),
unique: !!element.unique,
isRequired: !!element.isRequired,
showOnXlsxTemplate: !element.omitOnXlsxTemplate,
Expand All @@ -38,6 +40,9 @@ export const extractFields = async (object, fields, core): Promise<void> => {
...(element.hasOwnProperty('defaultValue')
? { defaultValue: element.defaultValue }
: {}),
...(element.oldName && element.oldName !== element.valueName
? { oldName: element.oldName }
: {}),
};
// ** Resource **
if (element.type === 'resource' || element.type === 'resources') {
Expand Down Expand Up @@ -217,8 +222,12 @@ export const extractFields = async (object, fields, core): Promise<void> => {
fields.push({
type: 'text',
name: `${element.valueName}_comment`,
oid: uuidv4(),
isCore: core,
generated: true,
...(element.oldName && element.oldName !== element.valueName
? { oldName: element.oldName }
: {}),
});
}
// ** Users **
Expand Down
15 changes: 10 additions & 5 deletions src/utils/form/getPreviousQuestion.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
/**
* Gets the previous question, from a question name.
* Gets the previous question, from a question unique id.
*
* @param structure parent structure.
* @param oid question unique id.
* @param name question name.
* @returns Previous question if exists.
*/
export const getPreviousQuestion = (structure: any, name: string): any => {
export const getPreviousQuestion = (
structure: any,
oid: string,
name: string
): any => {
if (structure.pages) {
for (const page of structure.pages) {
const question = getPreviousQuestion(page, name);
const question = getPreviousQuestion(page, oid, name);
if (question) return question;
}
} else if (structure.elements) {
for (const elementIndex in structure.elements) {
const element = structure.elements[elementIndex];
if (element.type === 'panel') {
if (element.name === name) return element;
const question = getPreviousQuestion(element, name);
if (element.oid === oid) return element;
const question = getPreviousQuestion(element, oid, name);
if (question) return question;
} else {
if (element.valueName === name) {
Expand Down
11 changes: 6 additions & 5 deletions src/utils/form/getQuestion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@
* Function by induction.
*
* @param structure structure of the form to search on
* @param name name of the field to search for
* @param oid unique id of the field to search for
* @param name name of the field
* @returns question definition
*/
export const getQuestion = (structure: any, name: string): any => {
export const getQuestion = (structure: any, oid: string, name: string): any => {
// Loop on elements to find the right question
if (structure.pages) {
for (const page of structure.pages) {
const question = getQuestion(page, name);
const question = getQuestion(page, oid, name);
if (question) return question;
}
} else if (structure.elements) {
for (const elementIndex in structure.elements) {
const element = structure.elements[elementIndex];
if (element.type === 'panel') {
if (element.name === name) return element;
const question = getQuestion(element, name);
if (element.oid === oid) return element;
const question = getQuestion(element, oid, name);
if (question) return question;
} else {
if (element.valueName === name) {
Expand Down
3 changes: 3 additions & 0 deletions src/utils/form/metadata.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import mongoose from 'mongoose';
import { sortBy } from 'lodash';
import extendAbilityForRecords from '@security/extendAbilityForRecords';
import { accessibleBy } from '@casl/mongoose';
import { v4 as uuidv4 } from 'uuid';

export type Metadata = {
automated?: boolean;
name: string;
type?: string;
oid?: string;
editor?: string;
filter?: { defaultOperator?: string; operators: string[] };
canSee?: boolean;
Expand Down Expand Up @@ -254,6 +256,7 @@ export const getMetaData = async (
const fieldMeta: Metadata = {
name: field.name,
type: field.type,
oid: uuidv4(),
editor: null,
usedIn: forms
.filter((form) => form.fields.find((x) => x.name === field.name))
Expand Down
Loading