Skip to content

Commit

Permalink
feat: add /validate path (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
BOLT04 authored Jan 17, 2022
1 parent 5333a20 commit 03c5c53
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 73 deletions.
43 changes: 42 additions & 1 deletion openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ paths:
$ref: '#/components/schemas/Template'
responses:
'200':
description: Template succesfully generated.
description: Template successfully generated.
content:
application/json:
schema:
Expand All @@ -60,6 +60,40 @@ paths:
schema:
$ref: '#/components/schemas/Problem'

/validate:
post:
summary: Validate the given AsyncAPI document.
operationId: validate
tags:
- validate
requestBody:
description: Validate the given AsyncAPI document with the AsyncAPI parser.
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ValidateDocument'
responses:
'204':
description: The given AsyncAPI document is valid.
'400':
description: The given AsyncAPI document is not valid.
content:
application/json:
schema:
$ref: '#/components/schemas/Problem'
'422':
description: The given AsyncAPI document is not valid due to invalid parameters in the request.
content:
application/json:
schema:
$ref: '#/components/schemas/Problem'
default:
description: Unexpected problem.
content:
application/json:
schema:
$ref: '#/components/schemas/Problem'
components:
schemas:
AsyncAPIDocument:
Expand Down Expand Up @@ -90,6 +124,13 @@ components:
which is usually located in the template's repository.
This field is optional but may be required for some templates.
additionalProperties: true
ValidateDocument:
type: object
required:
- asyncapi
properties:
asyncapi:
$ref: '#/components/schemas/AsyncAPIDocument'
Problem:
type: object
properties:
Expand Down
6 changes: 3 additions & 3 deletions src/controllers/generate.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class GenerateController implements Controller {
} catch (err) {
return next(err);
}

const zip = this.archiverService.createZip(res);

let tmpDir: string;
Expand Down Expand Up @@ -136,9 +136,9 @@ export class GenerateController implements Controller {
logger: false,
});
const router = Router();

router.post(
`${this.basepath}`,
`${this.basepath}`,
documentValidationMiddleware,
this.generate.bind(this)
);
Expand Down
8 changes: 4 additions & 4 deletions src/controllers/tests/generator.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('GeneratorController', () => {
it('should generate template ', async () => {
const app = new App([new GenerateController()]);

return await request(app.getServer())
return request(app.getServer())
.post('/generate')
.send({
asyncapi: {
Expand All @@ -32,7 +32,7 @@ describe('GeneratorController', () => {
it('should pass when sent template parameters are empty', async () => {
const app = new App([new GenerateController()]);

return await request(app.getServer())
return request(app.getServer())
.post('/generate')
.send({
asyncapi: {
Expand All @@ -51,7 +51,7 @@ describe('GeneratorController', () => {
it('should throw error when sent template parameters are invalid', async () => {
const app = new App([new GenerateController()]);

return await request(app.getServer())
return request(app.getServer())
.post('/generate')
.send({
asyncapi: {
Expand All @@ -76,7 +76,7 @@ describe('GeneratorController', () => {
instancePath: '',
schemaPath: '#/additionalProperties',
keyword: 'additionalProperties',
params: {
params: {
additionalProperty: 'customParameter'
},
message: 'must NOT have additional properties'
Expand Down
164 changes: 164 additions & 0 deletions src/controllers/tests/validate.controller.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import request from 'supertest';

import { App } from '../../app';
import { ProblemException } from '../../exceptions/problem.exception';

import { ValidateController } from '../validate.controller';

const validJSONAsyncAPI = {
asyncapi: '2.2.0',
info: {
title: 'Account Service',
version: '1.0.0',
description: 'This service is in charge of processing user signups'
},
channels: {
'user/signedup': {
subscribe: {
message: {
$ref: '#/components/messages/UserSignedUp'
}
}
}
},
components: {
messages: {
UserSignedUp: {
payload: {
type: 'object',
properties: {
displayName: {
type: 'string',
description: 'Name of the user'
},
email: {
type: 'string',
format: 'email',
description: 'Email of the user'
}
}
}
}
}
}
};
const validYAMLAsyncAPI = `
asyncapi: '2.2.0'
info:
title: Account Service
version: 1.0.0
description: This service is in charge of processing user signups
channels:
user/signedup:
subscribe:
message:
$ref: '#/components/messages/UserSignedUp'
components:
messages:
UserSignedUp:
payload:
type: object
properties:
displayName:
type: string
description: Name of the user
email:
type: string
format: email
description: Email of the user
`;
const invalidJSONAsyncAPI = {
asyncapi: '2.0.0',
info: {
tite: 'My API', // spelled wrong on purpose to throw an error in the test
version: '1.0.0'
},
channels: {}
};

describe('ValidateController', () => {
describe('[POST] /validate', () => {
it('should validate AsyncAPI document in JSON', async () => {
const app = new App([new ValidateController()]);

return request(app.getServer())
.post('/validate')
.send({
asyncapi: validJSONAsyncAPI
})
.expect(204);
});

it('should validate AsyncAPI document in YAML', async () => {
const app = new App([new ValidateController()]);

return request(app.getServer())
.post('/validate')
.send({
asyncapi: validYAMLAsyncAPI
})
.expect(204);
});

it('should throw error when sent an empty document', async () => {
const app = new App([new ValidateController()]);

return request(app.getServer())
.post('/validate')
.send({})
.expect(422, {
type: ProblemException.createType('invalid-request-body'),
title: 'Invalid Request Body',
status: 422,
validationErrors: [
{
instancePath: '',
schemaPath: '#/required',
keyword: 'required',
params: {
missingProperty: 'asyncapi'
},
message: 'must have required property \'asyncapi\''
}
]
});
});

it('should throw error when sent an invalid AsyncAPI document', async () => {
const app = new App([new ValidateController()]);

return request(app.getServer())
.post('/validate')
.send({
asyncapi: invalidJSONAsyncAPI
})
.expect(422, {
type: ProblemException.createType('validation-errors'),
title: 'There were errors validating the AsyncAPI document.',
status: 422,
validationErrors: [
{
title: '/info should NOT have additional properties',
location: {
jsonPointer: '/info'
}
},
{
title: '/info should have required property \'title\'',
location: {
jsonPointer: '/info'
}
}
],
parsedJSON: {
asyncapi: '2.0.0',
info: {
tite: 'My API',
version: '1.0.0'
},
channels: {}
}
});
});
});
});
34 changes: 34 additions & 0 deletions src/controllers/validate.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { NextFunction, Request, Response, Router } from 'express';

import { Controller } from '../interfaces';

import { parse, prepareParserConfig, tryConvertToProblemException } from '../utils/parser';

/**
* Controller which exposes the Parser functionality, to validate the AsyncAPI document.
*/
export class ValidateController implements Controller {
public basepath = '/validate';

private async validate(req: Request, res: Response, next: NextFunction) {
try {
const options = prepareParserConfig(req);
await parse(req.body?.asyncapi, options);

res.status(204).end();
} catch (err: unknown) {
return next(tryConvertToProblemException(err));
}
}

public boot(): Router {
const router = Router();

router.post(
`${this.basepath}`,
this.validate.bind(this)
);

return router;
}
}
Loading

0 comments on commit 03c5c53

Please sign in to comment.