diff --git a/src/i18n/en.json b/src/i18n/en.json index 240c0dd45..b51f17110 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -208,6 +208,9 @@ "invalidArguments": "Either data or version must be provided.", "wrongTemplateProvided": "Template must sharing the same resource as the parent form of the edited record." } + }, + "errors": { + "missingParameters": "Missing query parameters: record id or uniqueField and uniqueValue" } }, "reference": { diff --git a/src/i18n/test.json b/src/i18n/test.json index 344f9c8a0..a537d2cf2 100644 --- a/src/i18n/test.json +++ b/src/i18n/test.json @@ -208,6 +208,9 @@ "invalidArguments": "******", "wrongTemplateProvided": "******" } + }, + "errors": { + "missingParameters": "******" } }, "reference": { diff --git a/src/schema/mutation/editRecord.mutation.ts b/src/schema/mutation/editRecord.mutation.ts index 6402ee50f..68df382f8 100644 --- a/src/schema/mutation/editRecord.mutation.ts +++ b/src/schema/mutation/editRecord.mutation.ts @@ -1,5 +1,4 @@ import { - GraphQLNonNull, GraphQLID, GraphQLError, GraphQLString, @@ -57,7 +56,9 @@ export const hasInaccessibleFields = ( /** Arguments for the editRecord mutation */ type EditRecordArgs = { - id: string | Types.ObjectId; + id?: string | Types.ObjectId; + uniqueField?: string; + uniqueValue?: string; data?: any; version?: string | Types.ObjectId; template?: string | Types.ObjectId; @@ -72,7 +73,9 @@ type EditRecordArgs = { export default { type: RecordType, args: { - id: { type: new GraphQLNonNull(GraphQLID) }, + id: { type: GraphQLID }, + uniqueField: { type: GraphQLString }, + uniqueValue: { type: GraphQLString }, data: { type: GraphQLJSON }, version: { type: GraphQLID }, template: { type: GraphQLID }, @@ -90,10 +93,27 @@ export default { ); } - const user = context.user; + if (!(args.uniqueField && args.uniqueValue) && !args.id) { + throw new GraphQLError( + context.i18next.t('mutations.record.errors.missingParameters') + ); + } - // Get record and form - const oldRecord: Record = await Record.findById(args.id); + const user = context.user; + // Get record + let oldRecord: Record; + if (args.id) { + oldRecord = await Record.findById(args.id); + } else if (args.uniqueField && args.uniqueValue) { + const query = {}; + query[`data.${args.uniqueField}`] = args.uniqueValue; + oldRecord = await Record.findOne(query); + } + if (!oldRecord) { + throw new GraphQLError(context.i18next.t('common.errors.dataNotFound')); + } + const oldRecordId = oldRecord.id; + // Get form const parentForm: Form = await Form.findById( oldRecord.form, 'fields permissions resource structure' @@ -203,7 +223,9 @@ export default { update, ownership && { createdBy: { ...oldRecord.createdBy, ...ownership } } ); - const record = Record.findByIdAndUpdate(args.id, update, { new: true }); + const record = Record.findByIdAndUpdate(oldRecordId, update, { + new: true, + }); await version.save(); return await record; } else { @@ -234,7 +256,9 @@ export default { }, $push: { versions: version._id }, }; - const record = Record.findByIdAndUpdate(args.id, update, { new: true }); + const record = Record.findByIdAndUpdate(oldRecordId, update, { + new: true, + }); await version.save(); return await record; } diff --git a/src/schema/query/record.query.ts b/src/schema/query/record.query.ts index 0f0d01168..cb65b1fd5 100644 --- a/src/schema/query/record.query.ts +++ b/src/schema/query/record.query.ts @@ -1,4 +1,4 @@ -import { GraphQLNonNull, GraphQLID, GraphQLError } from 'graphql'; +import { GraphQLID, GraphQLError, GraphQLString } from 'graphql'; import { Form, Record } from '@models'; import { RecordType } from '../types'; import extendAbilityForRecords from '@security/extendAbilityForRecords'; @@ -10,7 +10,9 @@ import { Context } from '@server/apollo/context'; /** Arguments for the record query */ type RecordArgs = { - id: string | Types.ObjectId; + id?: string | Types.ObjectId; + uniqueField?: string; + uniqueValue?: string; }; /** @@ -20,14 +22,34 @@ type RecordArgs = { export default { type: RecordType, args: { - id: { type: new GraphQLNonNull(GraphQLID) }, + id: { type: GraphQLID }, + uniqueField: { type: GraphQLString }, + uniqueValue: { type: GraphQLString }, }, async resolve(parent, args: RecordArgs, context: Context) { graphQLAuthCheck(context); try { const user = context.user; // Get the form and the record - const record = await Record.findById(args.id); + if (!(args.uniqueField && args.uniqueValue) && !args.id) { + throw new GraphQLError( + context.i18next.t('mutations.record.errors.missingParameters') + ); + } + + let record: Record; + if (args.id) { + record = await Record.findById(args.id); + } else if (args.uniqueField && args.uniqueValue) { + const query = {}; + query[`data.${args.uniqueField}`] = args.uniqueValue; + record = await Record.findOne(query); + } + + if (!record) { + throw new GraphQLError(context.i18next.t('common.errors.dataNotFound')); + } + const form = await Form.findById(record.form); // Check ability