Skip to content

Commit

Permalink
feat: Implement middleware for schema validations (#306)
Browse files Browse the repository at this point in the history
* feat: Implement middleware for schema validations

* chore: Re add schema validations

* chore: Add tests

* chore: Remove schema test
  • Loading branch information
LautaroPetaccio committed Oct 14, 2021
1 parent 280cc80 commit 66d68ed
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 33 deletions.
21 changes: 0 additions & 21 deletions src/Item/Item.router.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,27 +429,6 @@ describe('Item router (old)', () => {
})
})

describe('when payload schema is invalid', () => {
it('should fail with invalid schema', async () => {
await testError(
{
query: {
id: 'id',
},
body: {
item: {
id: 'id',
},
},
auth: {
ethAddress: testItem.eth_address,
},
},
'Invalid schema'
)
})
})

describe('when is_approved is sent in the payload', () => {
it('should fail with cant set is_approved message', async () => {
await testError(
Expand Down
15 changes: 3 additions & 12 deletions src/Item/Item.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { HTTPError, STATUS_CODES } from '../common/HTTPError'
import { collectionAPI } from '../ethereum/api/collection'
import { Bridge } from '../ethereum/api/Bridge'
import { peerAPI } from '../ethereum/api/peer'
import { getValidator } from '../utils/validator'
import {
withModelAuthorization,
withAuthentication,
withModelExists,
AuthRequest,
withLowercasedParams,
withSchemaValidation,
} from '../middleware'
import { Ownable } from '../Ownable'
import { S3Item, getFileUploader, ACL, S3Content } from '../S3'
Expand All @@ -27,13 +27,11 @@ import { hasAccess as hasCollectionAccess } from '../Collection/access'
import { isCommitteeMember } from '../Committee'
import { Item } from './Item.model'
import { ItemAttributes } from './Item.types'
import { itemSchema } from './Item.schema'
import { upsertItemSchema } from './Item.schema'
import { FullItem } from './Item.types'
import { hasAccess } from './access'
import { getDecentralandItemURN, toDBItem } from './utils'

const validator = getValidator()

export class ItemRouter extends Router {
itemFilesRequestHandler:
| ((req: Request, res: Response) => Promise<boolean>) // Promisified RequestHandler
Expand Down Expand Up @@ -93,6 +91,7 @@ export class ItemRouter extends Router {
this.router.put(
'/items/:id',
withAuthentication,
withSchemaValidation(upsertItemSchema),
server.handleRequest(this.upsertItem)
)

Expand Down Expand Up @@ -315,14 +314,6 @@ export class ItemRouter extends Router {
)
}

const validate = validator.compile(itemSchema)

validate(itemJSON)

if (validate.errors) {
throw new HTTPError('Invalid schema', validate.errors)
}

const canUpsert = await new Ownable(Item).canUpsert(id, eth_address)

if (!canUpsert) {
Expand Down
9 changes: 9 additions & 0 deletions src/Item/Item.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,12 @@ export const itemSchema = Object.freeze({
'updated_at',
],
})

export const upsertItemSchema = Object.freeze({
type: 'object',
properties: {
item: itemSchema,
},
additionalProperties: false,
required: ['item'],
})
1 change: 1 addition & 0 deletions src/middleware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './asMiddleware'
export * from './authentication-legacy'
export * from './authentication'
export * from './authorization'
export * from './schemaValidator'
export * from './logger'
export * from './model'
export * from './url'
90 changes: 90 additions & 0 deletions src/middleware/schemaValidator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import supertest from 'supertest'
import express from 'express'
import { Schema } from 'ajv'
import { buildURL } from '../../spec/utils'
import { app } from '../server'
import { withSchemaValidation } from './schemaValidator'

const schema: Schema = Object.freeze({
type: 'object',
properties: {
id: { type: 'string' },
},
additionalProperties: false,
required: ['id'],
})

const simpleResponseHandler = (_: express.Request, res: express.Response) => {
res.status(200).end()
}

app
.getRouter()
.post('/test', withSchemaValidation(schema), simpleResponseHandler)
const server = supertest(app.getApp())

let data: any
describe('when posting invalid data', () => {
beforeEach(() => {
data = { id: 234 }
})

it('should respond with a 400 and a detailed description of the validation errors', () => {
return server
.post(buildURL('/test'))
.send(data)
.expect(400)
.then((response) => {
expect(response.body).toEqual({
error: 'Invalid request body',
data: [
{
dataPath: '/id',
keyword: 'type',
message: 'should be string',
params: { type: 'string' },
schemaPath: '#/properties/id/type',
},
],
ok: false,
})
})
})
})

describe('when posting no data', () => {
it('should respond with a 400 and a detailed description of the validation errors', () => {
return server
.post(buildURL('/test'))
.expect(400)
.then((response) => {
expect(response.body).toEqual({
error: 'Invalid request body',
data: [
{
dataPath: '',
keyword: 'required',
message: "should have required property 'id'",
params: { missingProperty: 'id' },
schemaPath: '#/required',
},
],
ok: false,
})
})
})
})

describe('when posting the correct data', () => {
beforeEach(() => {
data = { id: 'anId' }
})

it("should respond with the handler's response", () => {
return server
.post(buildURL('/test'))
.send(data)
.expect(200)
.then(() => undefined)
})
})
26 changes: 26 additions & 0 deletions src/middleware/schemaValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Schema } from 'ajv'
import express from 'express'
import { HTTPError, STATUS_CODES } from '../common/HTTPError'
import { getValidator } from '../utils/validator'

const validator = getValidator()

export const withSchemaValidation = (schema: Schema) => (
req: express.Request,
_: express.Response,
next: express.NextFunction
) => {
const validate = validator.compile(schema)
const valid = validate(req.body)

if (!valid) {
next(
new HTTPError(
'Invalid request body',
validate.errors,
STATUS_CODES.badRequest
)
)
}
next()
}

0 comments on commit 66d68ed

Please sign in to comment.