diff --git a/Dockerfile b/Dockerfile index a696aab01..293db7bbe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18-alpine as base +FROM node:22-alpine AS base WORKDIR /home/node/app @@ -10,7 +10,7 @@ COPY . . RUN mkdir -p files -FROM base as production +FROM base AS production ENV NODE_PATH=./build ENV NODE_CONFIG_DIR=/home/node/app/config diff --git a/src/index.ts b/src/index.ts index cb62deaf4..5df01c8c6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ import { SafeServer } from './server'; import mongoose from 'mongoose'; +// import { Record } from '@models'; import pullJobScheduler from './server/pullJobScheduler'; import customNotificationScheduler from './server/customNotificationScheduler'; import { startDatabase } from './server/database'; @@ -45,8 +46,16 @@ const launchServer = async () => { }; startDatabase(); -mongoose.connection.once('open', () => { +mongoose.connection.once('open', async () => { logger.log({ level: 'info', message: '📶 Connected to database' }); + // try { + // const collection = mongoose.connection.db.collection('records'); + // await collection.dropIndex('incrementalId_1_resource_1').catch(() => {}); + // await Record.syncIndexes(); + // logger.info('✅ Record indexes synced'); + // } catch (e) { + // logger.warn(`⚠️ Could not auto-sync Record indexes: ${e?.message || e}`); + // } launchServer(); // subscriberSafe(); pullJobScheduler(); diff --git a/src/models/index.ts b/src/models/index.ts index f40015fed..d16d59433 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -27,7 +27,7 @@ export * from './template.model'; export * from './distributionList.model'; export * from './customNotification.model'; export * from './layer.model'; -export * from './draftRecord.model'; +// export * from './draftRecord.model'; export * from './emailNotification.model'; export * from './activityLog.model'; export * from './emailDistributionList.model'; diff --git a/src/models/record.model.ts b/src/models/record.model.ts index 2b8770735..ba23ea957 100644 --- a/src/models/record.model.ts +++ b/src/models/record.model.ts @@ -22,6 +22,7 @@ export interface Record extends AccessibleFieldsDocument { createdAt: Date; modifiedAt: Date; archived: boolean; + draft: boolean; data: any; versions: any; permissions: { @@ -45,7 +46,7 @@ const recordSchema = new Schema( { incrementalId: { type: String, - required: true, + required: false, }, form: { type: mongoose.Schema.Types.ObjectId, @@ -97,6 +98,10 @@ const recordSchema = new Schema( type: Boolean, default: false, }, + draft: { + type: Boolean, + default: false, + }, data: { type: mongoose.Schema.Types.Mixed, required: true, @@ -112,7 +117,14 @@ const recordSchema = new Schema( ); recordSchema.index( { incrementalId: 1, resource: 1 }, - { unique: true, partialFilterExpression: { resource: { $exists: true } } } + { + unique: true, + partialFilterExpression: { + resource: { $exists: true }, + draft: false, + incrementalId: { $exists: true }, + }, + } ); recordSchema.index({ '$**': 'text' }); @@ -120,6 +132,7 @@ recordSchema.index({ 'data.$**': 1 }); recordSchema.index({ archived: 1, form: 1, resource: 1, createdAt: 1 }); recordSchema.index({ resource: 1, archived: 1 }); +recordSchema.index({ draft: 1, form: 1, resource: 1, createdAt: 1 }); recordSchema.index({ createdAt: 1 }); recordSchema.index({ form: 1 }); diff --git a/src/routes/download/index.ts b/src/routes/download/index.ts index dc01b8ebb..4b1eedb2e 100644 --- a/src/routes/download/index.ts +++ b/src/routes/download/index.ts @@ -127,6 +127,7 @@ router.get('/form/records/:id', async (req, res) => { const filter = { form: req.params.id, archived: { $ne: true }, + draft: { $ne: true }, ...Record.find(accessibleBy(formAbility, 'read').Record).getFilter(), }; const columns = await getColumns( @@ -187,6 +188,7 @@ router.get('/form/records/:id/history', async (req, res) => { const record: Record = await Record.findOne({ _id: req.params.id, archived: { $ne: true }, + draft: { $ne: true }, }) .populate({ path: 'versions', @@ -316,6 +318,7 @@ router.get('/resource/records/:id', async (req, res) => { records = await Record.find({ resource: req.params.id, archived: { $ne: true }, + draft: { $ne: true }, }); } const rows = await getRows(columns, records); diff --git a/src/routes/file/file.controller.ts b/src/routes/file/file.controller.ts index cc00caf14..c86fe04d1 100644 --- a/src/routes/file/file.controller.ts +++ b/src/routes/file/file.controller.ts @@ -78,6 +78,7 @@ export default class FileController extends BaseController { const associatedRecord = await Record.findOne({ resource: new Types.ObjectId(String(resourceId)), archived: { $ne: true }, + draft: { $ne: true }, $or: fileFieldQueries, }).select('_id'); diff --git a/src/schema/mutation/addRecord.mutation.ts b/src/schema/mutation/addRecord.mutation.ts index 7e752ad00..ed5c134d1 100644 --- a/src/schema/mutation/addRecord.mutation.ts +++ b/src/schema/mutation/addRecord.mutation.ts @@ -1,4 +1,9 @@ -import { GraphQLID, GraphQLNonNull, GraphQLError } from 'graphql'; +import { + GraphQLID, + GraphQLNonNull, + GraphQLError, + GraphQLBoolean, +} from 'graphql'; import GraphQLJSON from 'graphql-type-json'; import { RecordType } from '../types'; import { Form, Record, Notification, Channel } from '@models'; @@ -15,6 +20,7 @@ import { Context } from '@server/apollo/context'; type AddRecordArgs = { form?: string | Types.ObjectId; data: any; + draft?: boolean; }; /** @@ -27,6 +33,7 @@ export default { args: { form: { type: GraphQLID }, data: { type: new GraphQLNonNull(GraphQLJSON) }, + draft: { type: GraphQLBoolean }, }, async resolve(parent, args: AddRecordArgs, context: Context) { graphQLAuthCheck(context); @@ -48,6 +55,7 @@ export default { // Check unicity of record if ( + !args.draft && form.permissions.recordsUnicity && form.permissions.recordsUnicity.length > 0 && form.permissions.recordsUnicity[0].role @@ -60,7 +68,7 @@ export default { if (unicityFilters.length > 0) { const uniqueRecordAlreadyExists = await Record.exists({ $and: [ - { form: form._id, archived: { $ne: true } }, + { form: form._id, archived: { $ne: true }, draft: { $ne: true } }, { $or: unicityFilters }, ], }); @@ -75,9 +83,9 @@ export default { // Create the record instance transformRecord(args.data, form.fields); const record = new Record({ - incrementalId: await getNextId( - String(form.resource ? form.resource : args.form) - ), + incrementalId: args.draft + ? null + : await getNextId(String(form.resource ? form.resource : args.form)), form: args.form, //createdAt: new Date(), //modifiedAt: new Date(), @@ -109,26 +117,31 @@ export default { _id: form._id, name: form.name, }, + draft: args.draft || false, }); // Update the createdBy property if we pass some owner data const ownership = getOwnership(form.fields, args.data); if (ownership) { record.createdBy = { ...record.createdBy, ...ownership }; } - // send notifications to channel - const channel = await Channel.findOne({ form: form._id }); - if (channel) { - const notification = new Notification({ - action: `New record - ${form.name}`, - content: record, - //createdAt: new Date(), - channel: channel.id, - seenBy: [], - }); - await notification.save(); - const publisher = await pubsub(); - publisher.publish(channel.id, { notification }); + + // send notifications to channel (skip for draft records) + if (!args.draft) { + const channel = await Channel.findOne({ form: form._id }); + if (channel) { + const notification = new Notification({ + action: `New record - ${form.name}`, + content: record, + //createdAt: new Date(), + channel: channel.id, + seenBy: [], + }); + await notification.save(); + const publisher = await pubsub(); + publisher.publish(channel.id, { notification }); + } } + await record.save(); return record; } catch (err) { diff --git a/src/schema/mutation/deleteDraftRecord.mutation.ts b/src/schema/mutation/deleteDraftRecord.mutation.ts index 5a6b705ca..16fba76c3 100644 --- a/src/schema/mutation/deleteDraftRecord.mutation.ts +++ b/src/schema/mutation/deleteDraftRecord.mutation.ts @@ -2,8 +2,8 @@ import { GraphQLNonNull, GraphQLID, GraphQLError } from 'graphql'; import { logger } from '@services/logger.service'; import { graphQLAuthCheck } from '@schema/shared'; import { Context } from '@server/apollo/context'; -import { DraftRecordType } from '../types'; -import { DraftRecord } from '@models'; +import { DraftRecordType } from '../types/draftRecord.type'; +import { DraftRecord } from '../../models/draftRecord.model'; import { Types } from 'mongoose'; /** Arguments for the deleteRecord mutation */ @@ -24,7 +24,7 @@ export default { graphQLAuthCheck(context); try { // Get draft Record and associated form - const draftRecord = await DraftRecord.findById(args.id); + const draftRecord = DraftRecord.findById(args.id); return await DraftRecord.findByIdAndDelete(draftRecord._id); } catch (err) { logger.error(err.message, { stack: err.stack }); diff --git a/src/schema/mutation/editDraftRecord.mutation.ts b/src/schema/mutation/editDraftRecord.mutation.ts index fbb0d7f93..29a12b10a 100644 --- a/src/schema/mutation/editDraftRecord.mutation.ts +++ b/src/schema/mutation/editDraftRecord.mutation.ts @@ -52,7 +52,7 @@ export default { const draftRecord = DraftRecord.findByIdAndUpdate(args.id, update, { new: true, }); - return await draftRecord; + return draftRecord; } catch (err) { logger.error(err.message, { stack: err.stack }); if (err instanceof GraphQLError) { diff --git a/src/schema/mutation/editRecord.mutation.ts b/src/schema/mutation/editRecord.mutation.ts index b825d7518..e50bbad56 100644 --- a/src/schema/mutation/editRecord.mutation.ts +++ b/src/schema/mutation/editRecord.mutation.ts @@ -6,13 +6,23 @@ import { GraphQLBoolean, } from 'graphql'; import GraphQLJSON from 'graphql-type-json'; -import { Form, Record, Resource, Version } from '@models'; +import { + Form, + Record, + Resource, + Version, + Notification, + Channel, +} from '@models'; +import pubsub from '../../server/pubsub'; import extendAbilityForRecords from '@security/extendAbilityForRecords'; +import { getFormPermissionFilter } from '@utils/filter'; import { transformRecord, getOwnership, checkRecordValidation, checkRecordTriggers, + getNextId, } from '@utils/form'; import { RecordType } from '../types'; import { Types } from 'mongoose'; @@ -63,6 +73,7 @@ type EditRecordArgs = { template?: string | Types.ObjectId; lang?: string; draft?: boolean; + updateDraftStatus?: boolean; skipValidation: boolean; }; @@ -79,6 +90,7 @@ export default { template: { type: GraphQLID }, lang: { type: GraphQLString }, draft: { type: GraphQLBoolean }, + updateDraftStatus: { type: GraphQLBoolean }, skipValidation: { type: GraphQLBoolean, defaultValue: false }, }, async resolve(parent, args: EditRecordArgs, context: Context) { @@ -199,14 +211,77 @@ export default { }, }, }; + + if (args.updateDraftStatus !== undefined) { + update.draft = args.updateDraftStatus; + + if (oldRecord.draft === true && args.updateDraftStatus === false) { + if ( + parentForm.permissions.recordsUnicity && + parentForm.permissions.recordsUnicity.length > 0 && + parentForm.permissions.recordsUnicity[0].role + ) { + const unicityFilters = getFormPermissionFilter( + user, + parentForm, + 'recordsUnicity' + ); + if (unicityFilters.length > 0) { + const uniqueRecordAlreadyExists = await Record.exists({ + $and: [ + { + form: parentForm._id, + archived: { $ne: true }, + draft: { $ne: true }, + _id: { $ne: oldRecord._id }, + }, + { $or: unicityFilters }, + ], + }); + if (uniqueRecordAlreadyExists) { + throw new GraphQLError( + context.i18next.t('common.errors.permissionNotGranted') + ); + } + } + } + + if (!oldRecord.incrementalId) { + update.incrementalId = await getNextId( + String( + parentForm.resource ? parentForm.resource : oldRecord.form + ) + ); + } + } + } + const ownership = getOwnership(fields, args.data); // Update with template during merge Object.assign( update, ownership && { createdBy: { ...oldRecord.createdBy, ...ownership } } ); - const record = Record.findByIdAndUpdate(args.id, update, { new: true }); + const record = await Record.findByIdAndUpdate(args.id, update, { + new: true, + }); await version.save(); - return await record; + + if (oldRecord.draft === true && args.updateDraftStatus === false) { + const channel = await Channel.findOne({ form: parentForm._id }); + if (channel) { + const notification = new Notification({ + action: `New record published - ${parentForm.name}`, + content: record, + channel: channel.id, + seenBy: [], + }); + await notification.save(); + const publisher = await pubsub(); + publisher.publish(channel.id, { notification }); + } + } + + return record; } else { // Revert an old version const oldVersion = await Version.findOne({ @@ -235,9 +310,43 @@ export default { }, $push: { versions: version._id }, }; - const record = Record.findByIdAndUpdate(args.id, update, { new: true }); + + if (args.updateDraftStatus !== undefined) { + update.draft = args.updateDraftStatus; + + if (oldRecord.draft === true && args.updateDraftStatus === false) { + if (!oldRecord.incrementalId) { + update.incrementalId = await getNextId( + String( + parentForm.resource ? parentForm.resource : oldRecord.form + ) + ); + } + } + } + + const record = await Record.findByIdAndUpdate(args.id, update, { + new: true, + }); await version.save(); - return await record; + + // Send notification if publishing a draft + if (oldRecord.draft === true && args.updateDraftStatus === false) { + const channel = await Channel.findOne({ form: parentForm._id }); + if (channel) { + const notification = new Notification({ + action: `New record published - ${parentForm.name}`, + content: record, + channel: channel.id, + seenBy: [], + }); + await notification.save(); + const publisher = await pubsub(); + publisher.publish(channel.id, { notification }); + } + } + + return record; } } catch (err) { logger.error(err.message, { stack: err.stack }); diff --git a/src/schema/mutation/index.ts b/src/schema/mutation/index.ts index 5b5b522fb..e656c30f3 100644 --- a/src/schema/mutation/index.ts +++ b/src/schema/mutation/index.ts @@ -84,9 +84,9 @@ import editLayer from './editLayer.mutation'; import deleteLayer from './deleteLayer.mutation'; import editPageContext from './editPageContext.mutation'; import restorePage from './restorePage.mutation'; -import addDraftRecord from './addDraftRecord.mutation'; -import deleteDraftRecord from './deleteDraftRecord.mutation'; -import editDraftRecord from './editDraftRecord.mutation'; +// import addDraftRecord from './addDraftRecord.mutation'; +// import deleteDraftRecord from './deleteDraftRecord.mutation'; +// import editDraftRecord from './editDraftRecord.mutation'; import addDashboardTemplate from './addDashboardTemplate.mutation'; import deleteDashboardTemplates from './deleteDashboardTemplates.mutation'; import addEmailNotification from './addEmailNotification.mutation'; @@ -189,9 +189,9 @@ const Mutation = new GraphQLObjectType({ editLayer, deleteLayer, restorePage, - addDraftRecord, - deleteDraftRecord, - editDraftRecord, + // addDraftRecord, + // deleteDraftRecord, + // editDraftRecord, addEmailDistributionList, editEmailDistributionList, addCustomTemplate, diff --git a/src/schema/query/index.ts b/src/schema/query/index.ts index a7876a394..d6393f24b 100644 --- a/src/schema/query/index.ts +++ b/src/schema/query/index.ts @@ -36,7 +36,7 @@ import group from './group.query'; import groups from './groups.query'; import layers from './layers.query'; import layer from './layer.query'; -import draftRecords from './draftRecords.query'; +// import draftRecords from './draftRecords.query'; import referenceDataAggregation from './referenceDataAggregation.query'; import emailNotification from './emailNotification.query'; import emailNotifications from './emailNotifications.query'; @@ -89,7 +89,7 @@ const Query = new GraphQLObjectType({ recordHistory, layers, layer, - draftRecords, + // draftRecords, emailDistributionLists, customTemplates, }, diff --git a/src/schema/query/record.query.ts b/src/schema/query/record.query.ts index 0f0d01168..74faa620d 100644 --- a/src/schema/query/record.query.ts +++ b/src/schema/query/record.query.ts @@ -1,4 +1,9 @@ -import { GraphQLNonNull, GraphQLID, GraphQLError } from 'graphql'; +import { + GraphQLNonNull, + GraphQLID, + GraphQLError, + GraphQLBoolean, +} from 'graphql'; import { Form, Record } from '@models'; import { RecordType } from '../types'; import extendAbilityForRecords from '@security/extendAbilityForRecords'; @@ -11,6 +16,7 @@ import { Context } from '@server/apollo/context'; /** Arguments for the record query */ type RecordArgs = { id: string | Types.ObjectId; + draft?: boolean; }; /** @@ -21,13 +27,18 @@ export default { type: RecordType, args: { id: { type: new GraphQLNonNull(GraphQLID) }, + draft: { type: GraphQLBoolean }, }, 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); + const filter: any = { _id: args.id }; + if (args.draft !== undefined) { + filter.draft = args.draft; + } + const record = await Record.findOne(filter); const form = await Form.findById(record.form); // Check ability diff --git a/src/schema/query/records.query.ts b/src/schema/query/records.query.ts index 64492c092..d8173fe20 100644 --- a/src/schema/query/records.query.ts +++ b/src/schema/query/records.query.ts @@ -1,4 +1,4 @@ -import { GraphQLError, GraphQLList } from 'graphql'; +import { GraphQLError, GraphQLList, GraphQLBoolean } from 'graphql'; import { RecordType } from '../types'; import { Record } from '@models'; import extendAbilityForRecords from '@security/extendAbilityForRecords'; @@ -8,19 +8,34 @@ import { accessibleBy } from '@casl/mongoose'; import { graphQLAuthCheck } from '@schema/shared'; import { Context } from '@server/apollo/context'; +/** Arguments for the records query */ +type RecordsArgs = { + draft?: boolean; +}; + /** * List all records available for the logged user. * Throw GraphQL error if not logged. */ export default { type: new GraphQLList(RecordType), - async resolve(parent, args, context: Context) { + args: { + draft: { type: GraphQLBoolean }, + }, + async resolve(parent, args: RecordsArgs, context: Context) { graphQLAuthCheck(context); try { const user = context.user; const ability = await extendAbilityForRecords(user); - // Return the records - const records = await Record.find(accessibleBy(ability, 'read').Record); + + const filter = accessibleBy(ability, 'read').Record; + + Object.assign(filter, { archived: { $ne: true } }); + if (args.draft !== undefined) { + Object.assign(filter, { draft: args.draft }); + } + + const records = await Record.find(filter); return getAccessibleFields(records, ability); } catch (err) { logger.error(err.message, { stack: err.stack }); diff --git a/src/schema/query/recordsAggregation.query.ts b/src/schema/query/recordsAggregation.query.ts index 7529b3e6b..d73280b90 100644 --- a/src/schema/query/recordsAggregation.query.ts +++ b/src/schema/query/recordsAggregation.query.ts @@ -4,6 +4,7 @@ import { GraphQLInt, GraphQLNonNull, GraphQLString, + GraphQLBoolean, } from 'graphql'; import GraphQLJSON from 'graphql-type-json'; import mongoose from 'mongoose'; @@ -63,6 +64,7 @@ type RecordsAggregationArgs = { sortField?: string; sortOrder?: string; contextFilters?: CompositeFilterDescriptor; + draft?: boolean; }; /** @@ -160,6 +162,7 @@ export default { sortField: { type: GraphQLString }, sortOrder: { type: GraphQLString }, at: { type: GraphQLDate }, + draft: { type: GraphQLBoolean }, }, async resolve(parent, args: RecordsAggregationArgs, context: Context) { graphQLAuthCheck(context); @@ -213,8 +216,12 @@ export default { Object.assign( mongooseFilter, { resource: new mongoose.Types.ObjectId(args.resource) }, - { archived: { $ne: true } } + { archived: { $ne: true }, draft: { $ne: true } } ); + + if (args.draft !== undefined) { + Object.assign(mongooseFilter, { draft: args.draft }); + } } else { throw new GraphQLError(context.i18next.t('common.errors.dataNotFound')); } diff --git a/src/schema/types/form.type.ts b/src/schema/types/form.type.ts index 9911c6053..9459efb83 100644 --- a/src/schema/types/form.type.ts +++ b/src/schema/types/form.type.ts @@ -87,7 +87,10 @@ export const FormType = new GraphQLObjectType({ if (args.archived) { Object.assign(mongooseFilter, { archived: true }); } else { - Object.assign(mongooseFilter, { archived: { $ne: true } }); + Object.assign(mongooseFilter, { + archived: { $ne: true }, + draft: { $ne: true }, + }); } if (args.filter) { mongooseFilter = { @@ -153,6 +156,7 @@ export const FormType = new GraphQLObjectType({ const count = await Record.find({ form: parent.id, archived: { $ne: true }, + draft: { $ne: true }, ...accessibleBy(ability, 'read').Record, }).count(); return count; @@ -221,7 +225,11 @@ export const FormType = new GraphQLObjectType({ if (unicityFilters.length > 0) { const record = await Record.findOne({ $and: [ - { form: parent._id, archived: { $ne: true } }, + { + form: parent._id, + archived: { $ne: true }, + draft: { $ne: true }, + }, { $or: unicityFilters }, ], }); diff --git a/src/schema/types/index.ts b/src/schema/types/index.ts index e96bb2417..997df2532 100644 --- a/src/schema/types/index.ts +++ b/src/schema/types/index.ts @@ -28,5 +28,5 @@ export * from './distributionList.type'; export * from './customNotification.type'; export * from './metadata.type'; export * from './layer.type'; -export * from './draftRecord.type'; +// export * from './draftRecord.type'; export * from './emailNotification.type'; diff --git a/src/schema/types/record.type.ts b/src/schema/types/record.type.ts index ca254dd3d..a5ffef32b 100644 --- a/src/schema/types/record.type.ts +++ b/src/schema/types/record.type.ts @@ -23,6 +23,7 @@ export const RecordType = new GraphQLObjectType({ createdAt: { type: GraphQLString }, modifiedAt: { type: GraphQLString }, archived: { type: GraphQLBoolean }, + draft: { type: GraphQLBoolean }, form: { type: FormType, async resolve(parent, args, context) { @@ -65,6 +66,7 @@ export const RecordType = new GraphQLObjectType({ const record = await Record.findOne({ _id: parent.data[name], archived: { $ne: true }, + draft: { $ne: true }, }); res[name] = record.data[field.displayField]; } catch { diff --git a/src/schema/types/resource.type.ts b/src/schema/types/resource.type.ts index c53814239..9e881b871 100644 --- a/src/schema/types/resource.type.ts +++ b/src/schema/types/resource.type.ts @@ -183,7 +183,10 @@ export const ResourceType = new GraphQLObjectType({ if (args.archived) { Object.assign(mongooseFilter, { archived: true }); } else { - Object.assign(mongooseFilter, { archived: { $ne: true } }); + Object.assign(mongooseFilter, { + archived: { $ne: true }, + draft: { $ne: true }, + }); } if (args.filter) { mongooseFilter = { @@ -244,6 +247,7 @@ export const ResourceType = new GraphQLObjectType({ const count = await Record.find({ resource: parent.id, archived: { $ne: true }, + draft: { $ne: true }, ...accessibleBy(ability, 'read').Record, }).count(); return count; diff --git a/src/utils/files/resourceExporter.ts b/src/utils/files/resourceExporter.ts index 9582bb2f8..ba0d20e20 100644 --- a/src/utils/files/resourceExporter.ts +++ b/src/utils/files/resourceExporter.ts @@ -479,7 +479,7 @@ export default class Exporter { $in: ids, }, }, - { archived: { $ne: true } }, + { archived: { $ne: true }, draft: { $ne: true } }, permissionFilters, ], }, diff --git a/src/utils/history/recordHistory.ts b/src/utils/history/recordHistory.ts index 8b43b19bb..4c6c79706 100644 --- a/src/utils/history/recordHistory.ts +++ b/src/utils/history/recordHistory.ts @@ -416,7 +416,11 @@ export class RecordHistory { const recordFilters = Record.find( accessibleBy(this.options.ability, 'read').Record ) - .where({ _id: { $in: ids }, archived: { $ne: true } }) + .where({ + _id: { $in: ids }, + archived: { $ne: true }, + draft: { $ne: true }, + }) .getFilter(); const records: Record[] = await Record.find(recordFilters); return records.map((record) => record.incrementalId); diff --git a/src/utils/schema/resolvers/Entity/index.ts b/src/utils/schema/resolvers/Entity/index.ts index d538c8d14..6c408e399 100644 --- a/src/utils/schema/resolvers/Entity/index.ts +++ b/src/utils/schema/resolvers/Entity/index.ts @@ -60,7 +60,11 @@ export const getEntityResolver = ( // Else, do db query const recordId = get(entity.data, fieldName.slice(0, -3), null); const record = recordId - ? await Record.findOne({ _id: recordId, archived: { $ne: true } }) + ? await Record.findOne({ + _id: recordId, + archived: { $ne: true }, + draft: { $ne: true }, + }) : null; return record; }, @@ -110,7 +114,7 @@ export const getEntityResolver = ( Object.assign( mongooseFilter, { _id: { $in: recordIds } }, - { archived: { $ne: true } } + { archived: { $ne: true }, draft: { $ne: true } } ); return await Record.find(mongooseFilter) .sort([[getSortField(args.sortField), args.sortOrder]]) @@ -279,7 +283,7 @@ export const getEntityResolver = ( { form: ids[entityName] }, ], }, - { archived: { $ne: true } } + { archived: { $ne: true }, draft: { $ne: true } } ); mongooseFilter[`data.${x.name}`] = entity.id.toString(); const records = await Record.find(mongooseFilter) diff --git a/src/utils/schema/resolvers/Query/all.ts b/src/utils/schema/resolvers/Query/all.ts index 79273687b..1f3a5ba82 100644 --- a/src/utils/schema/resolvers/Query/all.ts +++ b/src/utils/schema/resolvers/Query/all.ts @@ -633,6 +633,7 @@ export default (entityName: string, fieldsByName: any, idsByName: any) => { $or: [{ _id: { $in: relatedIds } }, ...relatedFilters], archived: { $ne: true }, + draft: { $ne: true }, }, projection ); diff --git a/src/utils/schema/resolvers/Query/single.ts b/src/utils/schema/resolvers/Query/single.ts index 5d3f148a1..b2a5cdb50 100644 --- a/src/utils/schema/resolvers/Query/single.ts +++ b/src/utils/schema/resolvers/Query/single.ts @@ -13,7 +13,11 @@ export default () => async (_, { id, data }, context) => { graphQLAuthCheck(context); try { - const record = await Record.findOne({ _id: id, archived: { $ne: true } }); + const record = await Record.findOne({ + _id: id, + archived: { $ne: true }, + draft: { $ne: true }, + }); if (data) { record.data = data; }