829 lines
28 KiB
TypeScript
829 lines
28 KiB
TypeScript
import { v4 as uuid } from 'uuid';
|
|
import { Collection, FilterQuery, UpdateQuery } from 'mongodb';
|
|
import { classToPlain, Exclude, plainToClassFromExist } from 'class-transformer';
|
|
import { Field, ID, ObjectType, Authorized } from 'type-graphql';
|
|
import DB, { createSetRequest } from '@seed/services/database/DBService';
|
|
|
|
import { Permission } from '@seed/interfaces/permission';
|
|
import { newError } from '@seed/helpers/Error';
|
|
|
|
import { parsePaginationOptions } from '@seed/helpers/Request';
|
|
import { GetArgs, GetManyArgs } from './Request';
|
|
import { addPermissions, addPermissionToQuery, checkPermissions, checkOrganisationPermissions } from './AccessService';
|
|
import { ApolloContext } from '@seed/interfaces/context';
|
|
import _ from 'lodash';
|
|
import { StreamOperationType, PostHookStatus } from '@seed/services/change-stream/change-stream.components';
|
|
import { asyncHook } from '@seed/services/hooks/hooks.decorator';
|
|
import { AccountTypeEnum } from '@src/accounts/account.components';
|
|
import { promiseAll } from '@seed/helpers/Utils';
|
|
import { EnginePathComponent } from '@seed/interfaces/components';
|
|
|
|
export interface BaseInterface {
|
|
_id: string;
|
|
|
|
updatedAt: Date;
|
|
createdAt: Date;
|
|
|
|
collectionName: string;
|
|
}
|
|
@ObjectType()
|
|
export abstract class BaseGraphModel implements BaseInterface {
|
|
@Exclude()
|
|
public collectionName: string;
|
|
@Exclude()
|
|
protected permissions: Permission;
|
|
@Exclude()
|
|
defaultSort: string;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@Field(() => ID)
|
|
_id: string;
|
|
|
|
@Field(() => ID, { nullable: true })
|
|
organisationId?: string;
|
|
|
|
@Field(() => ID, { nullable: true })
|
|
createdBy?: string;
|
|
|
|
@Field(() => ID, { nullable: true })
|
|
updatedBy?: string;
|
|
|
|
@Field(() => ID, { nullable: true })
|
|
deletedBy?: string;
|
|
|
|
@Field()
|
|
createdAt: Date;
|
|
|
|
@Field()
|
|
updatedAt: Date;
|
|
|
|
@Field(() => [String])
|
|
public r: string[];
|
|
|
|
@Authorized('admin')
|
|
@Field(() => [String])
|
|
public w: string[];
|
|
|
|
@Authorized('admin')
|
|
@Field(() => [String])
|
|
public d: string[];
|
|
|
|
paths: EnginePathComponent[];
|
|
|
|
public constructor(init: { collectionName: string; permissions: Permission; defaultSort?: string }) {
|
|
this.collectionName = init.collectionName;
|
|
this.permissions = init.permissions;
|
|
|
|
this.r = init.permissions.r;
|
|
this.w = init.permissions.w;
|
|
this.d = init.permissions.d;
|
|
|
|
this.createdAt = new Date();
|
|
this.updatedAt = new Date();
|
|
|
|
this.defaultSort = init.defaultSort || 'createdAt desc';
|
|
}
|
|
|
|
/*
|
|
*
|
|
* Model functions
|
|
*
|
|
*/
|
|
|
|
public get(): any {
|
|
return classToPlain(this);
|
|
}
|
|
|
|
public set(doc: any): void {
|
|
plainToClassFromExist(this, doc);
|
|
|
|
if (doc._id) this._id = doc._id;
|
|
|
|
if (doc.r) this.r = _.uniq(_.concat(this.r, doc.r));
|
|
if (doc.w) this.w = _.uniq(_.concat(this.w, doc.w));
|
|
if (doc.d) this.d = _.uniq(_.concat(this.d, doc.d));
|
|
|
|
if (doc.organisationId) this.organisationId = doc.organisationId;
|
|
|
|
if (doc.createdAt) this.createdAt = doc.createdAt;
|
|
if (doc.updatedAt) this.updatedAt = doc.updatedAt;
|
|
}
|
|
|
|
abstract searchOptions(): string[];
|
|
abstract filterOptions(): string[];
|
|
|
|
searchEngine?(): any;
|
|
|
|
async prehook?(): Promise<void>;
|
|
async beforeCreate?(ctx?: ApolloContext | null): Promise<void>;
|
|
async beforeUpdate?(ctx?: ApolloContext | null): Promise<void>;
|
|
async beforeDelete?(ctx?: ApolloContext | null): Promise<void>;
|
|
|
|
async afterCreate?(changeStream: any): Promise<void>;
|
|
async afterUpdate?(changeStream: any): Promise<void>;
|
|
async afterDelete?(changeStream: any): Promise<void>;
|
|
|
|
/*
|
|
*
|
|
* DB Functions
|
|
*
|
|
*/
|
|
|
|
public async db(): Promise<Collection<this>> {
|
|
return await (await DB.getInstance()).db.collection<this>(this.collectionName);
|
|
}
|
|
|
|
public getPath(): EnginePathComponent[] {
|
|
if (this.paths) {
|
|
this.paths.push({
|
|
ressourceModel: this.collectionName,
|
|
ressourceId: this._id,
|
|
});
|
|
|
|
return this.paths;
|
|
} else
|
|
return [
|
|
{
|
|
ressourceModel: this.collectionName,
|
|
ressourceId: this._id,
|
|
},
|
|
];
|
|
}
|
|
|
|
public getQuery(query: FilterQuery<this>, ctx: ApolloContext | null): any {
|
|
let finalQuery: any;
|
|
|
|
if (ctx) {
|
|
const account = ctx.ctx.user;
|
|
const organisationId = ctx.ctx.organisationId;
|
|
|
|
// We add the permission (on r field) to make sure that kind of user has access to the ressource
|
|
finalQuery = addPermissionToQuery(account, 'get', { ...query });
|
|
|
|
// We check if it comes from an organisation
|
|
// Check of the user's correct authorisation on that organisation comes from the middleware
|
|
|
|
if (!ctx.ctx.noOrganisationCheck) {
|
|
if (organisationId) {
|
|
if (this.collectionName == 'accounts') finalQuery['organisationIds'] = organisationId;
|
|
else finalQuery['organisationId'] = { $eq: organisationId };
|
|
} else {
|
|
// If admin & public query -> do not add this condition and let search every records)
|
|
if (
|
|
account &&
|
|
account.types &&
|
|
!account.types.includes(AccountTypeEnum.public) &&
|
|
!account.types.includes(AccountTypeEnum.admin)
|
|
) {
|
|
finalQuery['organisationId'] = { $exists: false };
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
finalQuery = { ...query };
|
|
}
|
|
|
|
return finalQuery;
|
|
}
|
|
|
|
public async getMany(query: FilterQuery<this>, pagination: GetArgs | undefined, ctx: ApolloContext | null): Promise<any> {
|
|
const finalQuery = this.getQuery(query, ctx);
|
|
|
|
if (!pagination) pagination = { limit: 100, skip: 0 };
|
|
if (!pagination.sort) pagination.sort = this.defaultSort;
|
|
const paginationOption = parsePaginationOptions(pagination);
|
|
|
|
try {
|
|
console.log('search request', JSON.stringify(finalQuery, null, 2));
|
|
return await (await this.db()).find(finalQuery, paginationOption).toArray();
|
|
} catch (error) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public async getCount(query: FilterQuery<this>, ctx: ApolloContext | null): Promise<number> {
|
|
const finalQuery = this.getQuery(query, ctx);
|
|
|
|
try {
|
|
const result = (await (await this.db()).countDocuments(finalQuery)) as any;
|
|
console.log(result);
|
|
return result;
|
|
} catch (error) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public async getAll(query: FilterQuery<this>, ctx: ApolloContext | null): Promise<any> {
|
|
const finalQuery = this.getQuery(query, ctx);
|
|
try {
|
|
return await (await this.db()).find(finalQuery).toArray();
|
|
} catch (error) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public async getOne(query: FilterQuery<this>, ctx: ApolloContext | null): Promise<this> {
|
|
if (ctx) {
|
|
const account = ctx.ctx.user;
|
|
const organisationId = ctx.ctx.organisationId;
|
|
|
|
const doc = await (await this.db()).findOne(query);
|
|
if (!doc) throw newError(404, query);
|
|
|
|
this.set(doc);
|
|
|
|
if (!ctx.ctx.noOrganisationCheck) {
|
|
if (organisationId) {
|
|
if (this.collectionName != 'accounts') checkOrganisationPermissions(this, organisationId);
|
|
}
|
|
}
|
|
|
|
checkPermissions(this, account, 'r');
|
|
|
|
return this;
|
|
} else {
|
|
const doc = await (await this.db()).findOne(query);
|
|
if (!doc) throw newError(404, query);
|
|
|
|
this.set(doc);
|
|
return this;
|
|
}
|
|
}
|
|
|
|
public async saveOne(newData: Partial<this>, ctx: ApolloContext | null, upsert = true): Promise<this> {
|
|
if (ctx) {
|
|
const account = ctx.ctx.user;
|
|
const organisationId = ctx.ctx.organisationId;
|
|
|
|
addPermissions(this, ['r', 'w', 'd'], [account._id]);
|
|
|
|
if (organisationId) {
|
|
newData.organisationId = organisationId;
|
|
}
|
|
|
|
newData.createdBy = account._id;
|
|
}
|
|
|
|
this.set(newData);
|
|
|
|
// if (!ctx) ctx = await createApolloContext();
|
|
|
|
try {
|
|
if (this.prehook) await this.prehook();
|
|
if (this.beforeCreate) await this.beforeCreate(ctx);
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
|
|
const { _id, ...dataToSave } = this.get();
|
|
const savedId = _id ? _id : uuid();
|
|
|
|
try {
|
|
const result = await (await this.db()).findOneAndUpdate(
|
|
{ _id: savedId },
|
|
{
|
|
$set: dataToSave,
|
|
$setOnInsert: {
|
|
_id: savedId,
|
|
},
|
|
} as any,
|
|
{ upsert, returnOriginal: false },
|
|
);
|
|
|
|
if (result.ok == 0) throw newError(1000, result);
|
|
|
|
this.set({ _id: savedId });
|
|
|
|
const changeStreamId = uuid();
|
|
const changeStreamData = {
|
|
_id: changeStreamId,
|
|
operation: StreamOperationType.insert,
|
|
collection: this.collectionName,
|
|
documentKey: this._id,
|
|
insertedValues: this.get(),
|
|
hookStatus: PostHookStatus.new,
|
|
createdAt: new Date(),
|
|
updateAt: new Date(),
|
|
};
|
|
await (await DB.getInstance()).db.collection('stream.changes').insertOne(changeStreamData);
|
|
|
|
if (this.afterCreate && (await asyncHook(changeStreamData))) {
|
|
await this.afterCreate(changeStreamData);
|
|
|
|
await (await DB.getInstance()).db
|
|
.collection('stream.changes')
|
|
.updateOne({ _id: changeStreamId }, { $set: { hookStatus: PostHookStatus.completed } });
|
|
}
|
|
|
|
return this;
|
|
} catch (error) {
|
|
throw newError(1000, error);
|
|
}
|
|
}
|
|
|
|
public async saveMany(newDataArr: Partial<this>[], ctx: ApolloContext | null): Promise<any[]> {
|
|
// Initialize the change stream
|
|
const changeStreams: any[] = [];
|
|
const afterCreatePromises: any[] = [];
|
|
|
|
const prehooksPromises = newDataArr.map(async (newData) => {
|
|
const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
|
|
if (ctx) {
|
|
const account = ctx.ctx.user;
|
|
const organisationId = ctx.ctx.organisationId;
|
|
|
|
addPermissions(clone, ['r', 'w', 'd'], [account._id]);
|
|
|
|
if (organisationId) {
|
|
newData.organisationId = organisationId;
|
|
}
|
|
|
|
newData.createdBy = account._id;
|
|
}
|
|
|
|
clone.set(newData);
|
|
|
|
// if (!ctx) ctx = await createApolloContext();
|
|
|
|
if (clone.prehook) await clone.prehook();
|
|
if (clone.beforeCreate) await clone.beforeCreate(ctx);
|
|
|
|
const { _id, ...dataToSave } = clone.get();
|
|
const savedId = _id ? _id : uuid();
|
|
clone.set({ _id: savedId });
|
|
|
|
const changeStreamId = uuid();
|
|
const changeStreamData = {
|
|
_id: changeStreamId,
|
|
operation: StreamOperationType.insert,
|
|
collection: clone.collectionName,
|
|
documentKey: clone._id,
|
|
insertedValues: clone.get(),
|
|
hookStatus: PostHookStatus.new,
|
|
createdAt: new Date(),
|
|
updateAt: new Date(),
|
|
};
|
|
|
|
changeStreams.push(changeStreamData);
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
afterCreatePromises.push(async () => {
|
|
if (clone.afterCreate) {
|
|
const res = await asyncHook(changeStreamData);
|
|
|
|
if (res) {
|
|
await clone.afterCreate(changeStreamData);
|
|
|
|
await (await DB.getInstance()).db
|
|
.collection('stream.changes')
|
|
.updateOne({ _id: changeStreamId }, { $set: { hookStatus: PostHookStatus.completed } });
|
|
}
|
|
}
|
|
});
|
|
|
|
return clone.get();
|
|
});
|
|
|
|
try {
|
|
const dataArr = await Promise.all(prehooksPromises);
|
|
await (await this.db()).insertMany(dataArr, { ordered: false });
|
|
await (await DB.getInstance()).db.collection('stream.changes').insertMany(changeStreams, { ordered: false });
|
|
await promiseAll(afterCreatePromises);
|
|
return dataArr;
|
|
} catch (error) {
|
|
console.error(error);
|
|
throw newError(1000, error);
|
|
}
|
|
}
|
|
|
|
public async updateOne(query: FilterQuery<this>, newData: Partial<this>, ctx: ApolloContext | null): Promise<this> {
|
|
try {
|
|
const dataThatHaveBeenChanged = createSetRequest<this>('', { ...newData, updatedAt: new Date() });
|
|
let dataBeforeChange: any;
|
|
|
|
if (ctx) {
|
|
const account = ctx.ctx.user;
|
|
// Check if doc exists & correct permissions
|
|
if (!this._id) await this.getOne(query, ctx);
|
|
|
|
dataBeforeChange = this.get();
|
|
|
|
dataThatHaveBeenChanged.updatedBy = account._id;
|
|
|
|
this.set(dataThatHaveBeenChanged);
|
|
if (this.beforeUpdate) await this.beforeUpdate(ctx);
|
|
|
|
checkPermissions(this, account, 'w');
|
|
|
|
const result = await (await this.db()).findOneAndUpdate(
|
|
query,
|
|
{ $set: dataThatHaveBeenChanged },
|
|
{ upsert: false, returnOriginal: false },
|
|
);
|
|
if (result.ok != 1) throw newError(1000, result);
|
|
|
|
this.set(result.value);
|
|
} else {
|
|
if (!this._id) await this.getOne(query, null);
|
|
dataBeforeChange = this.get();
|
|
|
|
this.set(dataThatHaveBeenChanged);
|
|
if (this.beforeUpdate) await this.beforeUpdate();
|
|
|
|
const result = await (await this.db()).findOneAndUpdate(
|
|
query,
|
|
{ $set: dataThatHaveBeenChanged },
|
|
{ upsert: false, returnOriginal: false },
|
|
);
|
|
if (result.ok != 1) throw newError(1000, result);
|
|
this.set(result.value);
|
|
}
|
|
|
|
const changeStreamId = uuid();
|
|
const changeStreamData = {
|
|
_id: changeStreamId,
|
|
operation: StreamOperationType.update,
|
|
collection: this.collectionName,
|
|
documentKey: this._id,
|
|
updatedValues: dataThatHaveBeenChanged,
|
|
beforeUpdateValues: dataBeforeChange,
|
|
hookStatus: PostHookStatus.new,
|
|
createdAt: new Date(),
|
|
updateAt: new Date(),
|
|
};
|
|
await (await DB.getInstance()).db.collection('stream.changes').insertOne(changeStreamData);
|
|
|
|
if (this.afterUpdate && (await asyncHook(changeStreamData))) {
|
|
await this.afterUpdate(changeStreamData);
|
|
|
|
await (await DB.getInstance()).db
|
|
.collection('stream.changes')
|
|
.updateOne({ _id: changeStreamId }, { $set: { hookStatus: PostHookStatus.completed } });
|
|
}
|
|
|
|
return this;
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async updateOneCustom(query: FilterQuery<this>, set: UpdateQuery<this>, ctx: ApolloContext | null): Promise<this> {
|
|
try {
|
|
let dataBeforeChange: any;
|
|
|
|
if (ctx) {
|
|
const account = ctx.ctx.user;
|
|
// Check if doc exists
|
|
if (!this._id) await this.getOne(query, ctx);
|
|
dataBeforeChange = this.get();
|
|
|
|
checkPermissions(this, account, 'w');
|
|
|
|
// Validate new data that will go into the DB
|
|
// const validation = await this.validate();
|
|
// if (!validation.success) return validation;
|
|
|
|
// Prepare data
|
|
this.updatedAt = new Date();
|
|
|
|
const result = await (await this.db()).findOneAndUpdate(query, set, { upsert: false, returnOriginal: false });
|
|
if (result.ok != 1) throw newError(1000, result);
|
|
|
|
if (result.value) this.set(result.value);
|
|
} else {
|
|
if (!this._id) await this.getOne(query, null);
|
|
dataBeforeChange = this.get();
|
|
|
|
// Prepare data
|
|
this.updatedAt = new Date();
|
|
|
|
const result = await (await this.db()).findOneAndUpdate(query, set, { upsert: false, returnOriginal: false });
|
|
if (result.ok != 1) throw newError(1000, result);
|
|
|
|
if (result.value) this.set(result.value);
|
|
}
|
|
|
|
const changeStreamId = uuid();
|
|
const changeStreamData = {
|
|
_id: changeStreamId,
|
|
operation: StreamOperationType.update,
|
|
collection: this.collectionName,
|
|
documentKey: this._id,
|
|
hookStatus: PostHookStatus.new,
|
|
updatedValues: JSON.stringify(set),
|
|
beforeUpdateValues: dataBeforeChange,
|
|
createdAt: new Date(),
|
|
updateAt: new Date(),
|
|
};
|
|
await (await DB.getInstance()).db.collection('stream.changes').insertOne(changeStreamData);
|
|
if (this.afterUpdate && (await asyncHook(changeStreamData))) {
|
|
await this.afterUpdate(changeStreamData);
|
|
|
|
await (await DB.getInstance()).db
|
|
.collection('stream.changes')
|
|
.updateOne({ _id: changeStreamId }, { $set: { hookStatus: PostHookStatus.completed } });
|
|
}
|
|
|
|
return this;
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async deleteOne(query: FilterQuery<this>, ctx: ApolloContext | null): Promise<this> {
|
|
try {
|
|
if (ctx) {
|
|
const account = ctx.ctx.user;
|
|
if (!this._id) await this.getOne(query, ctx);
|
|
checkPermissions(this, account, 'd');
|
|
|
|
if (this.beforeDelete) await this.beforeDelete(ctx);
|
|
|
|
const result = await (await this.db()).findOneAndDelete(query);
|
|
|
|
if (result.ok != 1) throw newError(1001);
|
|
|
|
const deletedModel = '_deleted_' + this.collectionName;
|
|
await (await DB.getInstance()).db.collection(deletedModel).insertOne({ ...result.value, deletedBy: account._id });
|
|
} else {
|
|
if (!this._id) await this.getOne(query, null);
|
|
|
|
if (this.beforeDelete) await this.beforeDelete();
|
|
|
|
const result = await (await this.db()).findOneAndDelete(query);
|
|
|
|
if (result.ok != 1) throw newError(1001);
|
|
|
|
const deletedModel = '_deleted_' + this.collectionName;
|
|
await (await DB.getInstance()).db.collection(deletedModel).insertOne(result.value);
|
|
}
|
|
const changeStreamId = uuid();
|
|
|
|
const changeStreamData = {
|
|
_id: changeStreamId,
|
|
operation: StreamOperationType.delete,
|
|
collection: this.collectionName,
|
|
documentKey: this._id,
|
|
hookStatus: PostHookStatus.new,
|
|
beforeUpdateValues: this.get(),
|
|
createdAt: new Date(),
|
|
updateAt: new Date(),
|
|
};
|
|
await (await DB.getInstance()).db.collection('stream.changes').insertOne(changeStreamData);
|
|
|
|
if (this.afterDelete && (await asyncHook(changeStreamData))) {
|
|
await this.afterDelete(changeStreamData);
|
|
|
|
await (await DB.getInstance()).db
|
|
.collection('stream.changes')
|
|
.updateOne({ _id: changeStreamId }, { $set: { hookStatus: PostHookStatus.completed } });
|
|
}
|
|
|
|
return this;
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
* Permissions
|
|
*
|
|
*/
|
|
|
|
public async updateQueryWithPermission(query: FilterQuery<this>, ctx: ApolloContext): Promise<FilterQuery<this>> {
|
|
const account = ctx.ctx.user;
|
|
const organisationId = ctx.ctx.organisationId;
|
|
|
|
// We add the permission (on r field) to make sure that kind of user has access to the ressource
|
|
const finalQuery = addPermissionToQuery(account, 'get', { ...query });
|
|
|
|
// We check if it comes from an organisation
|
|
// Check of the user's correct authorisation on that organisation comes from the middleware
|
|
if (organisationId) {
|
|
finalQuery['organisationId'] = { $eq: organisationId };
|
|
} else {
|
|
finalQuery['organisationId'] = { $exists: false };
|
|
}
|
|
|
|
return finalQuery;
|
|
}
|
|
|
|
// public async updateOnePermission(
|
|
// query: FilterQuery<this>,
|
|
// add: 'add' | 'remove',
|
|
// type: 'r' | 'w' | 'd',
|
|
// newPerm: string,
|
|
// account?: AccountModel,
|
|
// ): Promise<this> {
|
|
// // Get the object from the DB
|
|
// try {
|
|
// // Check if doc exists
|
|
// // TODO : if (this.get() returns, then don't)
|
|
// await this.getOne(query, account);
|
|
// if (account) this.checkPermissions(account.get(), 'w');
|
|
|
|
// // Validate new data that will go into the DB
|
|
// // const validation = await this.validate();
|
|
// // if (!validation.success) return validation;
|
|
|
|
// // Prepare data
|
|
// this.updatedAt = new Date();
|
|
|
|
// if (add == 'add') {
|
|
// const result = await (await this.db()).findOneAndUpdate(
|
|
// query,
|
|
// { $addToSet: { [type]: newPerm } },
|
|
// { upsert: false, returnOriginal: false },
|
|
// );
|
|
// if (result.ok != 1) throw newError('general.dbError', '400', result);
|
|
|
|
// this.set(result.value);
|
|
// } else if (add == 'remove') {
|
|
// const result = await (await this.db()).findOneAndUpdate(
|
|
// query,
|
|
// { $pull: { [type]: newPerm } },
|
|
// { upsert: false, returnOriginal: false },
|
|
// );
|
|
// if (result.ok != 1) throw newError('general.dbError', '400', result);
|
|
|
|
// this.set(result.value);
|
|
// }
|
|
|
|
// return this;
|
|
// } catch (error) {
|
|
// throw error;
|
|
// }
|
|
// }
|
|
|
|
public async addOneSubModel<SUB extends BaseGraphModel>(
|
|
parentQ: { _id: string },
|
|
subModelRefField: string,
|
|
subModelFieldName: keyof this,
|
|
subModel: SUB,
|
|
subModelInput: any,
|
|
ctx: ApolloContext | null,
|
|
): Promise<SUB> {
|
|
try {
|
|
subModelInput[subModelRefField] = parentQ._id;
|
|
const subM = await subModel.saveOne(subModelInput, ctx);
|
|
await this.updateOneCustom(parentQ as any, { $push: { [`${subModelFieldName}`]: subM._id } } as any, null);
|
|
return subM;
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async editOneSubModel<SUB extends BaseGraphModel>(
|
|
subModel: SUB,
|
|
subModelId: string,
|
|
subModelInput: any,
|
|
ctx: ApolloContext | null,
|
|
): Promise<SUB> {
|
|
try {
|
|
// No need to check if belongs to the correct organisation, the middleware does that
|
|
const subM = await subModel.updateOne({ _id: subModelId } as any, subModelInput, ctx);
|
|
return subM;
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async deleteOneSubModel<SUB extends BaseGraphModel>(
|
|
parentQ: FilterQuery<this>,
|
|
subModelFieldName: keyof this,
|
|
subModel: SUB,
|
|
subModelId: string,
|
|
ctx: ApolloContext | null,
|
|
): Promise<SUB> {
|
|
try {
|
|
const subM = await subModel.deleteOne({ _id: subModelId } as any, ctx);
|
|
await this.updateOneCustom(parentQ, { $pull: { [`${subModelFieldName}`]: subM._id } } as any, null);
|
|
return subM;
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public getOneSubRessource(subRessoureFieldName: keyof this | string, subRessoureId: string): any | undefined {
|
|
try {
|
|
const subress = _.get(this, subRessoureFieldName);
|
|
if (subress) {
|
|
const ind = _.findIndex(subress, { _id: subRessoureId });
|
|
if (ind !== -1) return subress[ind];
|
|
}
|
|
return;
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public getDefaultSubRessource(subRessoureFieldName: keyof this | string): any | undefined {
|
|
try {
|
|
const subress = _.get(this, subRessoureFieldName);
|
|
if (subress) {
|
|
const ind = _.findIndex(subress, { default: true });
|
|
if (ind !== -1) return subress[ind];
|
|
return subress[0];
|
|
}
|
|
return;
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async addOneSubRessource(
|
|
parentQ: { _id: string },
|
|
subRessoureFieldName: keyof this | string,
|
|
subRessoure: any,
|
|
ctx: ApolloContext | null,
|
|
): Promise<any> {
|
|
try {
|
|
if (subRessoure.default && _.get(this, subRessoureFieldName)) {
|
|
// Remove all default from others
|
|
await this.updateOneCustom(
|
|
{ _id: this._id } as any,
|
|
{
|
|
$set: { [`${subRessoureFieldName}.$[].default`]: false } as any,
|
|
},
|
|
ctx,
|
|
);
|
|
} else subRessoure.default = false;
|
|
|
|
// Generate ID if not in the model
|
|
if (!subRessoure._id) subRessoure._id = uuid();
|
|
await this.updateOneCustom(parentQ as any, { $push: { [`${subRessoureFieldName}`]: subRessoure } } as any, ctx);
|
|
return subRessoure;
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async editOneSubRessource(
|
|
parentQ: { _id: string },
|
|
subRessoureFieldName: keyof this | string,
|
|
subRessoureId: string,
|
|
subRessoure: any,
|
|
ctx: ApolloContext | null,
|
|
): Promise<any> {
|
|
try {
|
|
if (!this.getOneSubRessource(subRessoureFieldName, subRessoureId)) throw newError(404);
|
|
|
|
if (subRessoure.default) {
|
|
// Remove all default from others
|
|
await this.updateOneCustom(
|
|
{ _id: this._id } as any,
|
|
{
|
|
$set: { [`${subRessoureFieldName}.$[].default`]: false } as any,
|
|
},
|
|
ctx,
|
|
);
|
|
} else subRessoure.default = false;
|
|
|
|
const filterQ = {
|
|
...parentQ,
|
|
[`${subRessoureFieldName}._id`]: subRessoureId,
|
|
};
|
|
await this.updateOneCustom(
|
|
filterQ as any,
|
|
{
|
|
$set: {
|
|
[`${subRessoureFieldName}.$`]: {
|
|
...subRessoure,
|
|
_id: subRessoureId,
|
|
},
|
|
} as any,
|
|
},
|
|
ctx,
|
|
);
|
|
return {
|
|
...subRessoure,
|
|
_id: subRessoureId,
|
|
};
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async deleteOneSubRessource(
|
|
parentQ: { _id: string },
|
|
subRessoureFieldName: keyof this | string,
|
|
subRessoureId: string,
|
|
ctx: ApolloContext | null,
|
|
): Promise<string> {
|
|
try {
|
|
if (!this.getOneSubRessource(subRessoureFieldName, subRessoureId)) throw newError(404);
|
|
|
|
const filterQ = {
|
|
...parentQ,
|
|
[`${subRessoureFieldName}._id`]: subRessoureId,
|
|
};
|
|
|
|
await this.updateOneCustom(filterQ as any, { $pull: { [`${subRessoureFieldName}`]: { _id: subRessoureId } } } as any, ctx);
|
|
return subRessoureId;
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|