Skip to content

Commit

Permalink
Merge pull request #151 from frouriojs/develop
Browse files Browse the repository at this point in the history
chore(release): 0.23.0
  • Loading branch information
solufa authored Feb 17, 2021
2 parents 8f2c406 + c2a7577 commit 28ee25c
Show file tree
Hide file tree
Showing 10 changed files with 467 additions and 202 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [0.23.0](https://github.com/frouriojs/frourio/compare/v0.22.2...v0.23.0) (2021-02-17)


### Features

* parse boolean type query ([1593ea1](https://github.com/frouriojs/frourio/commit/1593ea1a21b3b0c47a2db9a1c3e9a358575bdd85))


### Bug Fixes

* optimize calling query parser ([bb48f2a](https://github.com/frouriojs/frourio/commit/bb48f2ac57e5adc692e770bd2480b81210bd06de))
* update [email protected] ([d57b2aa](https://github.com/frouriojs/frourio/commit/d57b2aa358a526f77183218355999408650ebcf1))

### [0.22.2](https://github.com/frouriojs/frourio/compare/v0.22.1...v0.22.2) (2021-01-22)


Expand Down
121 changes: 96 additions & 25 deletions __test__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import fastify, { FastifyInstance } from 'fastify'
import FormData from 'form-data'
import axios from 'axios'
import aspida from '@aspida/axios'
import aspidaFetch from '@aspida/node-fetch'
import api from '../servers/all/api/$api'
import frourio from '../servers/all/$server'
import controller from '../servers/all/api/controller'

const port = 11111
const baseURL = `http://localhost:${port}`
const client = api(aspida(undefined, { baseURL }))
const fetchClient = api(aspidaFetch(undefined, { baseURL, throwHttpErrors: true }))
let server: FastifyInstance

beforeEach(cb => {
Expand All @@ -24,12 +26,35 @@ afterEach(cb => {
server.close(cb)
})

test('GET: 200', async () => {
const res = await client.$get({
query: { requiredNum: 1, requiredNumArr: [1, 2], id: '1', disable: 'false' }
})
expect(res?.id).toBe(1)
})
test('GET: 200', () =>
Promise.all(
[
{
requiredNum: 1,
requiredNumArr: [1, 2],
id: '1',
disable: 'false',
bool: true,
boolArray: [false, true]
},
{
requiredNum: 2,
emptyNum: 0,
requiredNumArr: [],
id: '1',
disable: 'false',
bool: false,
optionalBool: true,
boolArray: [],
optionalBoolArray: [true, false, false]
}
].map(query =>
Promise.all([
expect(client.$get({ query })).resolves.toEqual(query),
expect(fetchClient.$get({ query })).resolves.toEqual(query)
])
)
))

test('GET: string', async () => {
const text = 'test'
Expand All @@ -45,18 +70,50 @@ test('GET: params.userId', async () => {
expect(res.headers['content-type']).toBe('application/json; charset=utf-8')
})

test('GET: 400', async () => {
await Promise.all([
expect(
client.get({ query: { requiredNum: 0, requiredNumArr: [], id: '1', disable: 'no boolean' } })
).rejects.toHaveProperty('response.status', 400),
expect(
client.get({
query: { requiredNum: 1, requiredNumArr: [1, 2], id: 'no number', disable: 'true' }
})
).rejects.toHaveProperty('response.status', 400)
])
})
test('GET: 400', () =>
Promise.all(
[
{
requiredNum: 0,
requiredNumArr: [],
id: '1',
disable: 'no boolean',
bool: false,
boolArray: []
},
{
requiredNum: 0,
requiredNumArr: [],
id: '2',
disable: 'true',
bool: false,
boolArray: ['no boolean']
},
{
requiredNum: 0,
requiredNumArr: ['no number'],
id: '3',
disable: 'true',
bool: false,
boolArray: []
},
{
requiredNum: 1,
requiredNumArr: [1, 2],
id: 'no number',
disable: 'true',
bool: false,
boolArray: []
}
].map(query =>
Promise.all([
// @ts-expect-error
expect(client.get({ query })).rejects.toHaveProperty('response.status', 400),
// @ts-expect-error
expect(fetchClient.get({ query })).rejects.toHaveProperty('response.status', 400)
])
)
))

test('GET: 500', async () => {
await expect(client.$500.get()).rejects.toHaveProperty('response.status', 500)
Expand All @@ -76,7 +133,14 @@ test('POST: formdata', async () => {
form.append('file', fs.createReadStream(fileName))
const res = await axios.post(baseURL, form, {
headers: form.getHeaders(),
params: { requiredNum: 0, requiredNumArr: [], id: 1, disable: true }
params: {
requiredNum: 0,
requiredNumArr: [],
id: 1,
disable: true,
bool: false,
boolArray: []
}
})
expect(res.data.port).toBe(port)
expect(res.data.fileName).toBe(fileName)
Expand Down Expand Up @@ -131,16 +195,23 @@ test('controller dependency injection', async () => {
}
})
.inject(() => ({
log: (n: number) => {
val = n
return Promise.resolve(n)
log: n => {
val = +n * 2
return Promise.resolve(`${val}`)
}
}))(server)

await expect(
injectedController.get({
query: { id, requiredNum: 1, requiredNumArr: [0], disable: 'true' }
query: {
id,
requiredNum: 1,
requiredNumArr: [0],
disable: 'true',
bool: false,
boolArray: []
}
})
).resolves.toHaveProperty('body', { id: +id })
expect(val).toBe(+id)
).resolves.toHaveProperty('body.id', `${+id * 2}`)
expect(val).toBe(+id * 2)
})
25 changes: 14 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "frourio",
"version": "0.22.2",
"version": "0.23.0",
"description": "Fast and type-safe full stack framework, for TypeScript",
"author": "Solufa <[email protected]>",
"license": "MIT",
Expand Down Expand Up @@ -84,36 +84,39 @@
]
},
"dependencies": {
"aspida": "^1.3.0",
"aspida": "^1.5.0",
"velona": "^0.7.0"
},
"devDependencies": {
"@aspida/axios": "^1.3.0",
"@aspida/axios": "^1.5.0",
"@aspida/node-fetch": "^1.5.0",
"@types/busboy": "^0.2.3",
"@types/jest": "^26.0.20",
"@types/node-fetch": "^2.5.8",
"@types/rimraf": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^4.14.0",
"@typescript-eslint/eslint-plugin": "^4.15.0",
"@typescript-eslint/parser": "^4.15.0",
"axios": "^0.21.1",
"class-validator": "^0.13.1",
"eslint": "^7.18.0",
"eslint": "^7.20.0",
"eslint-config-prettier": "^7.2.0",
"eslint-config-standard": "^16.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.1.3",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-standard": "^5.0.0",
"fastify": "^3.10.1",
"fastify-multipart": "^3.3.1",
"fastify": "^3.12.0",
"fastify-multipart": "^4.0.0",
"form-data": "^3.0.0",
"jest": "^26.6.3",
"node-fetch": "^2.6.1",
"prettier": "^2.2.1",
"rimraf": "^3.0.2",
"standard-version": "^9.1.0",
"ts-jest": "^26.4.4",
"ts-jest": "^26.5.1",
"ts-node": "^9.1.1",
"typescript": "^4.1.3"
"typescript": "^4.1.5"
}
}
66 changes: 55 additions & 11 deletions servers/all/$server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,35 +74,67 @@ export type ServerMethods<T extends AspidaMethods, U extends Record<string, any>
) => ServerResponse<T[K]> | Promise<ServerResponse<T[K]>>
}

const parseNumberTypeQueryParams = (numberTypeParamsFn: (query: any) => ([string, boolean, boolean][])): preValidationHookHandler => (req, reply, done) => {
const parseNumberTypeQueryParams = (numberTypeParams: [string, boolean, boolean][]): preValidationHookHandler => (req, reply, done) => {
const query: any = req.query
const numberTypeParams = numberTypeParamsFn(query)

for (const [key, isOptional, isArray] of numberTypeParams) {
const param = query[key]
const param = isArray ? (query[`${key}[]`] ?? query[key]) : query[key]

if (isArray) {
if (!isOptional && param === undefined) {
query[key] = []
} else if (!isOptional || param !== undefined) {
if (!Array.isArray(param)) {
const vals = (Array.isArray(param) ? param : [param]).map(Number)

if (vals.some(isNaN)) {
reply.code(400).send()
return
}

const vals = (param as string[]).map(Number)
query[key] = vals as any
}

if (vals.some(isNaN)) {
delete query[`${key}[]`]
} else if (!isOptional || param !== undefined) {
const val = Number(param)

if (isNaN(val)) {
reply.code(400).send()
return
}

query[key] = val as any
}
}

done()
}

const parseBooleanTypeQueryParams = (booleanTypeParams: [string, boolean, boolean][]): preValidationHookHandler => (req, reply, done) => {
const query: any = req.query

for (const [key, isOptional, isArray] of booleanTypeParams) {
const param = isArray ? (query[`${key}[]`] ?? query[key]) : query[key]

if (isArray) {
if (!isOptional && param === undefined) {
query[key] = []
} else if (!isOptional || param !== undefined) {
const vals = (Array.isArray(param) ? param : [param]).map(p => p === 'true' ? true : p === 'false' ? false : null)

if (vals.some(v => v === null)) {
reply.code(400).send()
return
}

query[key] = vals as any
}

delete query[`${key}[]`]
} else if (!isOptional || param !== undefined) {
const val = Number(param)
const val = param === 'true' ? true : param === 'false' ? false : null

if (isNaN(val)) {
if (val === null) {
reply.code(400).send()
return
}
Expand All @@ -114,6 +146,14 @@ const parseNumberTypeQueryParams = (numberTypeParamsFn: (query: any) => ([string
done()
}

const callParserIfExistsQuery = (parser: preValidationHookHandler): preValidationHookHandler => (req, reply, done) =>
Object.keys(req.query as any).length ? parser(req, reply, done) : done()

const normalizeQuery: preValidationHookHandler = (req, _, done) => {
req.query = JSON.parse(JSON.stringify(req.query))
done()
}

const createTypedParamsHandler = (numberTypeParams: string[]): preValidationHookHandler => (req, reply, done) => {
const params = req.params as Record<string, string | number>

Expand Down Expand Up @@ -209,7 +249,9 @@ export default (fastify: FastifyInstance, options: FrourioOptions = {}) => {
onRequest: [...hooks0.onRequest, ctrlHooks0.onRequest],
preParsing: hooks0.preParsing,
preValidation: [
parseNumberTypeQueryParams(query => !Object.keys(query).length ? [] : [['requiredNum', false, false], ['optionalNum', true, false], ['optionalNumArr', true, true], ['emptyNum', true, false], ['requiredNumArr', false, true]]),
callParserIfExistsQuery(parseNumberTypeQueryParams([['requiredNum', false, false], ['optionalNum', true, false], ['optionalNumArr', true, true], ['emptyNum', true, false], ['requiredNumArr', false, true]])),
callParserIfExistsQuery(parseBooleanTypeQueryParams([['bool', false, false], ['optionalBool', true, false], ['boolArray', false, true], ['optionalBoolArray', true, true]])),
normalizeQuery,
createValidateHandler(req => [
Object.keys(req.query as any).length ? validateOrReject(Object.assign(new Validators.Query(), req.query as any), validatorOptions) : null
])
Expand All @@ -224,8 +266,10 @@ export default (fastify: FastifyInstance, options: FrourioOptions = {}) => {
onRequest: [...hooks0.onRequest, ctrlHooks0.onRequest],
preParsing: hooks0.preParsing,
preValidation: [
parseNumberTypeQueryParams(() => [['requiredNum', false, false], ['optionalNum', true, false], ['optionalNumArr', true, true], ['emptyNum', true, false], ['requiredNumArr', false, true]]),
parseNumberTypeQueryParams([['requiredNum', false, false], ['optionalNum', true, false], ['optionalNumArr', true, true], ['emptyNum', true, false], ['requiredNumArr', false, true]]),
parseBooleanTypeQueryParams([['bool', false, false], ['optionalBool', true, false], ['boolArray', false, true], ['optionalBoolArray', true, true]]),
formatMultipartData([]),
normalizeQuery,
createValidateHandler(req => [
validateOrReject(Object.assign(new Validators.Query(), req.query as any), validatorOptions),
validateOrReject(Object.assign(new Validators.Body(), req.body as any), validatorOptions)
Expand Down Expand Up @@ -273,7 +317,7 @@ export default (fastify: FastifyInstance, options: FrourioOptions = {}) => {
{
onRequest: hooks0.onRequest,
preParsing: hooks0.preParsing,
preValidation: parseNumberTypeQueryParams(query => !Object.keys(query).length ? [] : [['limit', true, false]])
preValidation: callParserIfExistsQuery(parseNumberTypeQueryParams([['limit', true, false]]))
},
methodToHandler(controller4.get)
)
Expand Down
Loading

0 comments on commit 28ee25c

Please sign in to comment.