Skip to content

Commit

Permalink
refactor: add idx property to ValidationErrorData; now ValidationEr…
Browse files Browse the repository at this point in the history
…ror is used also for *Many documents validation.

All validator records are now combined into one array like was suggested in #248 (comment)

One array better:
`errors: [ { idx: 3, message: '', path: ''} ]`

Than nested array with nulls:
`errors: [ null, null, null, { message: '', errors: [{ message: '', path: ''}] } ]`
  • Loading branch information
nodkz committed Sep 3, 2020
1 parent e697829 commit 5e9a7cd
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 107 deletions.
7 changes: 4 additions & 3 deletions src/__tests__/github_issues/248-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ describe("issue #248 - payloads' errors", () => {
message
path
value
idx
}
}
}
Expand All @@ -155,13 +156,13 @@ describe("issue #248 - payloads' errors", () => {
records: null,
error: {
__typename: 'ValidationError',
message: 'User validation failed: someStrangeField: this is a validate message',
message: 'Nothing has been saved. Some documents contain validation errors',
errors: [
{
message: 'this is a validate message',
path: '1.someStrangeField',
// ^^ - we add idx of broken record
path: 'someStrangeField',
value: 'Test',
idx: 1,
},
],
},
Expand Down
45 changes: 0 additions & 45 deletions src/errors/ManyValidationError.ts

This file was deleted.

47 changes: 1 addition & 46 deletions src/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import type { InterfaceTypeComposer, SchemaComposer } from 'graphql-compose';
import { MongoError, getMongoErrorOTC } from './MongoError';
import { ValidationError, getValidationErrorOTC } from './ValidationError';
import {
ManyValidationError,
getManyValidationErrorOTC,
ManyValidationsByIdx,
} from './ManyValidationError';
import { RuntimeError, getRuntimeErrorOTC } from './RuntimeError';

export { MongoError, ValidationError, ManyValidationError, ManyValidationsByIdx, RuntimeError };
export { MongoError, ValidationError, RuntimeError };

export function getErrorInterface(schemaComposer: SchemaComposer<any>): InterfaceTypeComposer {
const ErrorInterface = schemaComposer.getOrCreateIFTC('ErrorInterface', (iftc) => {
Expand Down Expand Up @@ -49,43 +44,3 @@ export function getErrorInterface(schemaComposer: SchemaComposer<any>): Interfac

return ErrorInterface;
}

export function getManyErrorInterface(schemaComposer: SchemaComposer<any>): InterfaceTypeComposer {
const ErrorInterface = schemaComposer.getOrCreateIFTC('ManyErrorInterface', (iftc) => {
iftc.addFields({
message: {
description: 'Generic error message',
type: 'String',
},
});

const ManyValidationErrorOTC = getManyValidationErrorOTC(schemaComposer);
const MongoErrorOTC = getMongoErrorOTC(schemaComposer);
const RuntimeErrorOTC = getRuntimeErrorOTC(schemaComposer);

ManyValidationErrorOTC.addInterface(iftc);
MongoErrorOTC.addInterface(iftc);
RuntimeErrorOTC.addInterface(iftc);

schemaComposer.addSchemaMustHaveType(ManyValidationErrorOTC);
schemaComposer.addSchemaMustHaveType(MongoErrorOTC);
schemaComposer.addSchemaMustHaveType(RuntimeErrorOTC);

const ManyValidationErrorType = ManyValidationErrorOTC.getType();
const MongoErrorType = MongoErrorOTC.getType();
const RuntimeErrorType = RuntimeErrorOTC.getType();

iftc.setResolveType((value) => {
switch (value?.name) {
case 'ManyValidationError':
return ManyValidationErrorType;
case 'MongoError':
return MongoErrorType;
default:
return RuntimeErrorType;
}
});
});

return ErrorInterface;
}
34 changes: 21 additions & 13 deletions src/resolvers/helpers/validate.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import type { Error as MongooseError } from 'mongoose';
import type { Document } from 'mongoose';
import { ValidationError, ManyValidationError, ManyValidationsByIdx } from '../../errors';
import { ValidationError } from '../../errors';

export type ValidationErrorData = {
path: string;
message: string;
value: any;
/**
* This `idx` property is used only for *Many operations.
* It stores idx from received array of records which occurs Validation Error.
*/
idx?: number;
};

export type ValidationsWithMessage = {
Expand Down Expand Up @@ -48,29 +53,32 @@ export async function validateAndThrow(doc: Document): Promise<void> {
/**
* Make async validation for array of mongoose documents.
* And if they have validation errors then throw one Error with embedding
* all validation errors for every document separately.
* If document does not have error then in embedded errors' array will
* be `null` at the same idx position.
* all validator errors into one array with addition of `idx` property.
* `idx` represent record index in array received from user.
*/
export async function validateManyAndThrow(docs: Document[]): Promise<void> {
const manyValidations: ManyValidationsByIdx = [];
const combinedValidators: Array<ValidationErrorData> = [];
let hasValidationError = false;

for (const doc of docs) {
const validations: ValidationsWithMessage | null = await validateDoc(doc);
for (let idx = 0; idx < docs.length; idx++) {
const validations: ValidationsWithMessage | null = await validateDoc(docs[idx]);

if (validations) {
manyValidations.push(validations);
validations.errors.forEach((validatorError) => {
combinedValidators.push({
...validatorError,
idx,
});
});

hasValidationError = true;
} else {
manyValidations.push(null);
}
}

if (hasValidationError) {
throw new ManyValidationError({
message: 'Some documents contain validation errors',
errors: manyValidations,
throw new ValidationError({
message: 'Nothing has been saved. Some documents contain validation errors',
errors: combinedValidators,
});
}
}

0 comments on commit 5e9a7cd

Please sign in to comment.