-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
update: fix async-app app chaining when mounting sub app #45
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -239,4 +239,30 @@ Scenario: get negative TODO permissions with errors | |||||
} | ||||||
} | ||||||
} | ||||||
""" | ||||||
""" | ||||||
|
||||||
Scenario: mounted apps (nest level 1) | ||||||
Given app setting "trust proxy" is enabled | ||||||
Given the request header X-Forwarded-For is "1.1.1.1" | ||||||
When GET /sub/test | ||||||
Then the response is 200 | ||||||
And the response payload includes | ||||||
""" | ||||||
{ | ||||||
"route": "/sub/test", | ||||||
"ip": "1.1.1.1" | ||||||
} | ||||||
""" | ||||||
|
||||||
Scenario: mounted apps (nest level 2) | ||||||
Given app setting "trust proxy" is enabled | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NITPICK:
Suggested change
|
||||||
Given the request header X-Forwarded-For is "10.0.1.3" | ||||||
When GET /sub/sub/test | ||||||
Then the response is 200 | ||||||
And the response payload includes | ||||||
""" | ||||||
{ | ||||||
"route": "/sub/sub/test", | ||||||
"ip": "10.0.1.3" | ||||||
} | ||||||
""" |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,7 +25,7 @@ interface AsyncOptions<TEntities extends Entities, TSchema> { | |
mapAsyncResultFn?: MapAsyncResultFn<TEntities>; | ||
} | ||
|
||
type Options<TEntities extends Entities> = { | ||
type Options<TEntities extends Entities> = { | ||
statusCode: number; | ||
isLastMiddleware: boolean; | ||
validateSchema?: ValidateSchema; | ||
|
@@ -36,11 +36,11 @@ const copyDecorators = <TSrc extends Decorator, TDest extends Decorator>( | |
src: TSrc, | ||
dest: TDest, | ||
) => { | ||
const keys = Object | ||
.keys(src) | ||
.filter(k => k.startsWith('$')) as (keyof Decorator)[]; | ||
const keys = Object.keys(src).filter(k => | ||
k.startsWith('$'), | ||
) as (keyof Decorator)[]; | ||
|
||
keys.forEach(k => dest[k] = src[k]); | ||
keys.forEach(k => (dest[k] = src[k])); | ||
}; | ||
|
||
const mapMiddleware = <TEntities extends Entities>( | ||
|
@@ -64,16 +64,16 @@ const mapMiddleware = <TEntities extends Entities>( | |
const isAsync = isAsyncMiddleware(middleware); | ||
|
||
const mappedVal = options.mapAsyncResultFn | ||
? await options.mapAsyncResultFn(result, { | ||
isAsyncMiddleware: isAsync, | ||
isLastMiddleware: options.isLastMiddleware, | ||
req, | ||
}) | ||
: result; | ||
? await options.mapAsyncResultFn(result, { | ||
isAsyncMiddleware: isAsync, | ||
isLastMiddleware: options.isLastMiddleware, | ||
req, | ||
}) | ||
: result; | ||
|
||
let val = mappedVal; | ||
let isRaw = false; | ||
let headers: Record<string, string|string[]> | undefined; | ||
let headers: Record<string, string | string[]> | undefined; | ||
|
||
if (mappedVal instanceof CustomResponse) { | ||
val = mappedVal.value; | ||
|
@@ -125,7 +125,9 @@ const mapMiddleware = <TEntities extends Entities>( | |
res.json(val || {}); | ||
} | ||
} catch (err) { | ||
if (!err || !(err instanceof CustomError)) { return next(err); } | ||
if (!err || !(err instanceof CustomError)) { | ||
return next(err); | ||
} | ||
|
||
const { statusCode, error, extra } = err; | ||
|
||
|
@@ -148,42 +150,48 @@ const mapMiddleware = <TEntities extends Entities>( | |
}; | ||
|
||
export default <TEntities extends Entities, TSchema>({ | ||
errorHandler, | ||
compileSchema, | ||
mapAsyncResultFn, | ||
}: AsyncOptions<TEntities, TSchema> = {}): Converter<TEntities, TSchema> => | ||
(args, context) => { | ||
const statusCode = args.find(isNumber) || DEFAULT_STATUS_CODE; | ||
|
||
const schema = args | ||
.filter(isSchema<TSchema>()) | ||
.find(s => s.$scope === 'response'); | ||
const rawSchema = schema | ||
errorHandler, | ||
compileSchema, | ||
mapAsyncResultFn, | ||
}: AsyncOptions<TEntities, TSchema> = {}): Converter<TEntities, TSchema> => ( | ||
args, | ||
context, | ||
) => { | ||
if (context.method === 'use') { | ||
return args; | ||
} | ||
Comment on lines
+160
to
+162
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be a little bit too eager — one other solution would be to make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is fine! |
||
|
||
const statusCode = args.find(isNumber) || DEFAULT_STATUS_CODE; | ||
|
||
const schema = args | ||
.filter(isSchema<TSchema>()) | ||
.find(s => s.$scope === 'response'); | ||
const rawSchema = schema | ||
? schema.$schema | ||
? schema.$schema | ||
? schema.$schema | ||
: schema | ||
: undefined; | ||
const validateSchema = | ||
compileSchema && rawSchema && compileSchema(rawSchema, context); | ||
|
||
const middlewares = args.filter(isMiddleware); | ||
const lastMiddleware = middlewares[middlewares.length - 1]; | ||
|
||
// 4 argument middlewares are error handlers, and we leave them untouched | ||
return args.map(m => | ||
isMiddleware(m) && m.length < 4 | ||
? mapMiddleware( | ||
m, | ||
// Check if this is the last valid middleware (not the statusCode arg) | ||
{ | ||
isLastMiddleware: m === lastMiddleware, | ||
mapAsyncResultFn, | ||
statusCode, | ||
validateSchema, | ||
}, | ||
// Passes a custom error handler | ||
errorHandler, | ||
) | ||
: m, | ||
); | ||
}; | ||
: schema | ||
: undefined; | ||
const validateSchema = | ||
compileSchema && rawSchema && compileSchema(rawSchema, context); | ||
|
||
const middlewares = args.filter(isMiddleware); | ||
const lastMiddleware = middlewares[middlewares.length - 1]; | ||
|
||
// 4 argument middlewares are error handlers, and we leave them untouched | ||
return args.map(m => | ||
isMiddleware(m) && m.length < 4 | ||
? mapMiddleware( | ||
m, | ||
// Check if this is the last valid middleware (not the statusCode arg) | ||
{ | ||
isLastMiddleware: m === lastMiddleware, | ||
mapAsyncResultFn, | ||
statusCode, | ||
validateSchema, | ||
}, | ||
// Passes a custom error handler | ||
errorHandler, | ||
) | ||
: m, | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NITPICK: