Skip to content

Commit

Permalink
Merge pull request #56 from frouriojs/develop
Browse files Browse the repository at this point in the history
chore(release): 0.23.0
  • Loading branch information
solufa committed Feb 17, 2021
2 parents 349b847 + 1f57067 commit d1ce9d1
Show file tree
Hide file tree
Showing 10 changed files with 411 additions and 164 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
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-express/compare/v0.22.2...v0.23.0) (2021-02-17)


### Features

* parse boolean type query ([adbc3f2](https://github.com/frouriojs/frourio-express/commit/adbc3f27da3d37ee29adec446181c7666b55a212))


### Bug Fixes

* optimize calling query parser ([054ca07](https://github.com/frouriojs/frourio-express/commit/054ca07d3196275b522709618dafe29be0c66312))
* update aspida and fast-json-stringify ([4302cc5](https://github.com/frouriojs/frourio-express/commit/4302cc592ccf7f4253379171be986826a3a01d86))

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


Expand Down
121 changes: 96 additions & 25 deletions __test__/index.spec.ts
Expand Up @@ -6,13 +6,15 @@ import express from 'express'
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: Server

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}`)
}
}))(express())

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)
})
23 changes: 13 additions & 10 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "frourio-express",
"version": "0.22.2",
"version": "0.23.0",
"description": "Fast and type-safe full stack framework, for TypeScript",
"author": "Solufa <solufa2020@gmail.com>",
"license": "MIT",
Expand Down Expand Up @@ -84,38 +84,41 @@
]
},
"dependencies": {
"aspida": "^1.3.0",
"fast-json-stringify": "^2.4.1",
"aspida": "^1.5.0",
"fast-json-stringify": "^2.4.2",
"velona": "^0.7.0"
},
"devDependencies": {
"@aspida/axios": "^1.3.0",
"@aspida/axios": "^1.5.0",
"@aspida/node-fetch": "^1.5.0",
"@types/express": "^4.17.7",
"@types/jest": "^26.0.20",
"@types/multer": "^1.4.5",
"@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",
"express": "^4.17.1",
"form-data": "^3.0.0",
"jest": "^26.6.3",
"multer": "^1.4.2",
"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"
}
}
43 changes: 37 additions & 6 deletions servers/all/$server.ts
Expand Up @@ -78,9 +78,7 @@ export type ServerMethods<T extends AspidaMethods, U extends Record<string, any>
) => ServerResponse<T[K]> | Promise<ServerResponse<T[K]>>
}

const parseNumberTypeQueryParams = (numberTypeParamsFn: (query: Request['query']) => ([string, boolean, boolean][])): RequestHandler => ({ query }, res, next) => {
const numberTypeParams = numberTypeParamsFn(query)

const parseNumberTypeQueryParams = (numberTypeParams: [string, boolean, boolean][]): RequestHandler => ({ query }, res, next) => {
for (const [key, isOptional, isArray] of numberTypeParams) {
const param = query[key]

Expand Down Expand Up @@ -108,6 +106,37 @@ const parseNumberTypeQueryParams = (numberTypeParamsFn: (query: Request['query']
next()
}

const parseBooleanTypeQueryParams = (booleanTypeParams: [string, boolean, boolean][]): RequestHandler => ({ query }, res, next) => {
for (const [key, isOptional, isArray] of booleanTypeParams) {
const param = query[key]

if (isArray) {
if (!isOptional && param === undefined) {
query[key] = []
} else if (!isOptional || param !== undefined) {
if (!Array.isArray(param)) return res.sendStatus(400)

const vals = (param as string[]).map(p => p === 'true' ? true : p === 'false' ? false : null)

if (vals.some(v => v === null)) return res.sendStatus(400)

query[key] = vals as any
}
} else if (!isOptional || param !== undefined) {
const val = param === 'true' ? true : param === 'false' ? false : null

if (val === null) return res.sendStatus(400)

query[key] = val as any
}
}

next()
}

const callParserIfExistsQuery = (parser: RequestHandler): RequestHandler => (req, res, next) =>
Object.keys(req.query).length ? parser(req, res, next) : next()

const parseJSONBoby: RequestHandler = (req, res, next) => {
express.json()(req, res, err => {
if (err) return res.sendStatus(400)
Expand Down Expand Up @@ -257,7 +286,8 @@ export default (app: Express, options: FrourioOptions = {}) => {
...hooks0.onRequest,
ctrlHooks0.onRequest,
hooks0.preParsing,
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]])),
createValidateHandler(req => [
Object.keys(req.query).length ? validateOrReject(Object.assign(new Validators.Query(), req.query), validatorOptions) : null
]),
Expand All @@ -268,7 +298,8 @@ export default (app: Express, options: FrourioOptions = {}) => {
...hooks0.onRequest,
ctrlHooks0.onRequest,
hooks0.preParsing,
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]]),
uploader,
formatMulterData([]),
createValidateHandler(req => [
Expand Down Expand Up @@ -306,7 +337,7 @@ export default (app: Express, options: FrourioOptions = {}) => {
app.get(`${basePath}/texts`, [
...hooks0.onRequest,
hooks0.preParsing,
parseNumberTypeQueryParams(query => !Object.keys(query).length ? [] : [['limit', true, false]]),
callParserIfExistsQuery(parseNumberTypeQueryParams([['limit', true, false]])),
methodToHandler(controller4.get)
])

Expand Down
37 changes: 35 additions & 2 deletions servers/all/api/controller.ts
Expand Up @@ -14,7 +14,40 @@ const responseSchema = defineResponseSchema(() => ({
type: 'object',
properties: {
id: {
type: 'string'
},
emptyNum: {
type: 'number'
},
requiredNum: {
type: 'number'
},
requiredNumArr: {
type: 'array',
items: {
type: 'number'
}
},
bool: {
type: 'boolean'
},
optionalBool: {
type: 'boolean'
},
boolArray: {
type: 'array',
items: {
type: 'boolean'
}
},
optionalBoolArray: {
type: 'array',
items: {
type: 'boolean'
}
},
disable: {
type: 'string'
}
}
}
Expand All @@ -23,14 +56,14 @@ const responseSchema = defineResponseSchema(() => ({

export default defineController(
{
log: (n: number) => {
log: (n: string) => {
console.log(n)
return Promise.resolve(n)
}
},
({ log }) => ({
get: async v => {
return { status: 200, body: { id: await log(+(v.query?.id || 0)) } }
return { status: 200, body: v.query && { ...v.query, id: await log(v.query.id) } }
},
post: v => ({
// @ts-expect-error
Expand Down
2 changes: 1 addition & 1 deletion servers/all/api/index.ts
Expand Up @@ -4,7 +4,7 @@ export type Methods = {
get: {
query?: Query
status: 200
resBody?: { id: number }
resBody?: Query
}

post: {
Expand Down

0 comments on commit d1ce9d1

Please sign in to comment.