Skip to content

Commit

Permalink
RND-613 Update API to use pre-generated JSON Schema for PUT requests (#…
Browse files Browse the repository at this point in the history
…284)

* RND-613 Update API to use pre-generated JSON Schema for PUT requests

- Update meadowlark-core package version
- Separate schemas to validate document for insert and update.

* Add tests to validate documentValidationForUpdate

* Update schema name to match new fields

* Update to use frontendRequest action to validate document
  • Loading branch information
jleiva-gap authored Aug 9, 2023
1 parent 14c6e51 commit 1a3628c
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 55 deletions.
6 changes: 3 additions & 3 deletions Meadowlark-js/packages/meadowlark-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
"@edfi/ed-fi-model-3.1": "3.0.1",
"@edfi/ed-fi-model-3.3b": "3.0.1",
"@edfi/meadowlark-utilities": "^v0.3.6-pre-36",
"@edfi/metaed-core": "^4.2.2-dev.2",
"@edfi/metaed-plugin-edfi-api-schema": "^4.2.2-dev.2",
"@edfi/metaed-plugin-edfi-unified": "^4.2.2-dev.2",
"@edfi/metaed-core": "^4.2.2-dev.3",
"@edfi/metaed-plugin-edfi-api-schema": "^4.2.2-dev.3",
"@edfi/metaed-plugin-edfi-unified": "^4.2.2-dev.3",
"@isaacs/ttlcache": "^1.4.1",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { MiddlewareModel } from '../middleware/MiddlewareModel';
import { parsePath } from '../middleware/ParsePathMiddleware';
import { parseBody } from '../middleware/ParseBodyMiddleware';
import { resourceValidation } from '../middleware/ValidateResourceMiddleware';
import { documentValidation, documentUpdateValidation } from '../middleware/ValidateDocumentMiddleware';
import { documentValidation } from '../middleware/ValidateDocumentMiddleware';
import { FrontendRequest } from './FrontendRequest';
import { FrontendResponse } from './FrontendResponse';
import * as Upsert from './Upsert';
Expand Down Expand Up @@ -67,7 +67,7 @@ function putStack(): MiddlewareStack {
R.andThen(logRequestBody),
R.andThen(resourceValidation),
R.andThen(metaEdModelFinding),
R.andThen(documentUpdateValidation),
R.andThen(documentValidation),
R.andThen(equalityConstraintValidation),
R.andThen(documentInfoExtraction),
R.andThen(getDocumentStore().securityMiddleware),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,14 @@ export function matchResourceNameToMetaEd(
* Validate the JSON body of the request against the Joi schema for the MetaEd entity corresponding
* to the API endpoint.
*/
export function validateEntityBodyAgainstSchema(metaEdModel: TopLevelEntity, body: object): ValidationError[] | null {
const schema = metaEdModel.data.edfiApiSchema.jsonSchema;
export function validateEntityBodyAgainstSchema(
metaEdModel: TopLevelEntity,
body: object,
isUpdate: boolean = false,
): ValidationError[] | null {
const schema = isUpdate
? metaEdModel.data.edfiApiSchema.jsonSchemaForUpdate
: metaEdModel.data.edfiApiSchema.jsonSchemaForInsert;

const validateFunction: ValidateFunction = ajv().compile(schema);
const isValid: boolean = validateFunction(body);
Expand All @@ -97,7 +103,7 @@ export function validateQueryParametersAgainstSchema(
let errors: string[] = [];

const schema = {
...metaEdModel.data.edfiApiSchema.jsonSchema,
...metaEdModel.data.edfiApiSchema.jsonSchemaForInsert,
// Need to relax the validation such that no fields are "required"
required: [],
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@
import { validateDocument } from '../validation/DocumentValidator';
import { writeDebugObject, writeRequestToLog } from '../Logger';
import { MiddlewareModel } from './MiddlewareModel';
import { FrontendRequest } from '../handler/FrontendRequest';

const moduleName = 'core.middleware.ValidateDocumentMiddleware';

/**
* Validates JSON document shape
*/
export async function documentValidation({ frontendRequest, frontendResponse }: MiddlewareModel): Promise<MiddlewareModel> {
const isUpdate = frontendRequest.action === 'updateById';
// if there is a response already posted, we are done
if (frontendResponse != null) return { frontendRequest, frontendResponse };
writeRequestToLog(moduleName, frontendRequest, 'documentValidation');
const error: object | null = await validateDocument(
frontendRequest.middleware.parsedBody,
frontendRequest.middleware.matchingMetaEdModel,
isUpdate,
);

if (error != null) {
Expand All @@ -33,42 +34,3 @@ export async function documentValidation({ frontendRequest, frontendResponse }:

return { frontendRequest, frontendResponse: null };
}

/**
* Validates JSON document shape for the update function
*/
export async function documentUpdateValidation({
frontendRequest,
frontendResponse,
}: MiddlewareModel): Promise<MiddlewareModel> {
const id = {
type: 'string',
description: 'The item id',
};
// Add id to schema to validate document
const frontendRequestUpdate: FrontendRequest = {
...frontendRequest,
middleware: {
...frontendRequest.middleware,
matchingMetaEdModel: {
...frontendRequest.middleware.matchingMetaEdModel,
data: {
...frontendRequest.middleware.matchingMetaEdModel.data,
edfiApiSchema: {
...frontendRequest.middleware.matchingMetaEdModel.data.edfiApiSchema,
jsonSchema: {
...frontendRequest.middleware.matchingMetaEdModel.data.edfiApiSchema.jsonSchema,
properties: {
id,
...frontendRequest.middleware.matchingMetaEdModel.data.edfiApiSchema.jsonSchema.properties,
},
required: ['id', ...frontendRequest.middleware.matchingMetaEdModel.data.edfiApiSchema.jsonSchema.required],
},
},
},
},
},
};

return documentValidation({ frontendRequest: frontendRequestUpdate, frontendResponse });
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export type ValidationFailure = { error: ValidationError[] | null };
export async function validateDocument(
body: object,
matchingMetaEdModel: TopLevelEntity,
isUpdate: boolean = false,
): Promise<ValidationFailure | null> {
const validationErrors: ValidationError[] | null = validateEntityBodyAgainstSchema(matchingMetaEdModel, body);
const validationErrors: ValidationError[] | null = validateEntityBodyAgainstSchema(matchingMetaEdModel, body, isUpdate);
return validationErrors == null ? null : { error: validationErrors };
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,34 @@ const testModel = (): TopLevelEntity => ({
referenceGroups: [],
descriptorCollectedApiProperties: [],
},
jsonSchema: {
jsonSchemaForInsert: {
$schema: 'https://json-schema.org/draft/2020-12/schema',
additionalProperties: false,
description: 'doc',
properties: {
uniqueId: {
description: 'doc',
maxLength: 30,
type: 'string',
},
someBooleanParameter: {
description: 'doc',
type: 'boolean',
},
someIntegerParameter: {
description: 'doc',
type: 'integer',
},
someDecimalParameter: {
description: 'doc',
type: 'number',
},
},
required: ['uniqueId'],
title: 'EdFi.Student',
type: 'object',
},
jsonSchemaForUpdate: {
$schema: 'https://json-schema.org/draft/2020-12/schema',
additionalProperties: false,
description: 'doc',
Expand Down Expand Up @@ -80,7 +107,7 @@ const testSchoolModel = (): TopLevelEntity => ({
resourceName: 'School',
},
},
jsonSchema: {
jsonSchemaForInsert: {
$schema: 'https://json-schema.org/draft/2020-12/schema',
additionalProperties: false,
description: 'doc',
Expand All @@ -107,6 +134,37 @@ const testSchoolModel = (): TopLevelEntity => ({
title: 'EdFi.Student',
type: 'object',
},
jsonSchemaForUpdate: {
$schema: 'https://json-schema.org/draft/2020-12/schema',
additionalProperties: false,
description: 'doc',
properties: {
id: {
description: 'The item id',
type: 'string',
},
uniqueId: {
description: 'doc',
maxLength: 30,
type: 'string',
},
someBooleanParameter: {
description: 'doc',
type: 'boolean',
},
someIntegerParameter: {
description: 'doc',
type: 'integer',
},
someDecimalParameter: {
description: 'doc',
type: 'number',
},
},
required: ['id', 'uniqueId'],
title: 'EdFi.Student',
type: 'object',
},
},
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const createModel = (): TopLevelEntity => ({
],
data: {
edfiApiSchema: {
jsonSchema: {
jsonSchemaForInsert: {
$schema: 'https://json-schema.org/draft/2020-12/schema',
additionalProperties: false,
description: 'doc',
Expand All @@ -44,6 +44,37 @@ const createModel = (): TopLevelEntity => ({
title: 'EdFi.Student',
type: 'object',
},
jsonSchemaForUpsert: {
$schema: 'https://json-schema.org/draft/2020-12/schema',
additionalProperties: false,
description: 'doc',
properties: {
id: {
description: 'The item id',
type: 'string',
},
uniqueId: {
description: 'doc',
maxLength: 30,
type: 'string',
},
someBooleanParameter: {
description: 'doc',
type: 'boolean',
},
someIntegerParameter: {
description: 'doc',
type: 'integer',
},
someDecimalParameter: {
description: 'doc',
type: 'number',
},
},
required: ['id', 'uniqueId'],
title: 'EdFi.Student',
type: 'object',
},
},
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { FrontendResponse, newFrontendResponse } from '../../src/handler/Fronten
import { FrontendRequest, newFrontendRequest } from '../../src/handler/FrontendRequest';
import { MiddlewareModel } from '../../src/middleware/MiddlewareModel';

describe('given a previous middleware has created a response', () => {
describe('given a previous middleware for Insert has created a response', () => {
const frontendRequest: FrontendRequest = newFrontendRequest();
const frontendResponse: FrontendResponse = newFrontendResponse();
let resultChain: MiddlewareModel;
Expand Down Expand Up @@ -95,7 +95,7 @@ describe('given an error response and document info from documentValidation', ()
});
});

describe('given a valid response from documentValidation', () => {
describe('given a valid response from documentValidation for Insert', () => {
const frontendRequest: FrontendRequest = newFrontendRequest();
const headerMetadata = {};
let resultChain: MiddlewareModel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const createModel = (): TopLevelEntity => ({
properties: [{ ...newEntityProperty(), metaEdName: 'uniqueId', isPartOfIdentity: true }],
data: {
edfiApiSchema: {
jsonSchema: {
jsonSchemaForInsert: {
$schema: 'https://json-schema.org/draft/2020-12/schema',
additionalProperties: false,
description: 'doc',
Expand All @@ -37,6 +37,34 @@ const createModel = (): TopLevelEntity => ({
title: 'EdFi.Student',
type: 'object',
},
jsonSchemaForUpdate: {
$schema: 'https://json-schema.org/draft/2020-12/schema',
additionalProperties: false,
description: 'doc',
properties: {
id: {
description: 'The item id',
type: 'string',
},
uniqueId: {
description: 'doc',
maxLength: 30,
type: 'string',
},
name: {
description: 'doc',
maxLength: 50,
type: 'string',
},
age: {
description: 'doc',
type: 'integer',
},
},
required: ['id', 'uniqueId'],
title: 'EdFi.Student',
type: 'object',
},
},
},
});
Expand Down

0 comments on commit 1a3628c

Please sign in to comment.