FFFF feat(transport-commons): add `context.http.response` by vonagam · Pull Request #2524 · feathersjs/feathers · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 5 additions & 6 deletions packages/express/src/rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,13 @@ const serviceMiddleware = (): RequestHandler => {
const contextBase = createContext(service, method, { http: {} });
res.hook = contextBase;

const context = await (service as any)[method](...args, contextBase);
const context = await (service as any)[method](...args, contextBase);
res.hook = context;

const result = http.getData(context);
const statusCode = http.getStatusCode(context, result);

res.data = result;
res.statusCode = statusCode;
const response = http.getResponse(context);
res.statusCode = response.status;
res.set(response.headers);
res.data = response.body;

return next();
});
Expand Down
21 changes: 20 additions & 1 deletion packages/express/test/rest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ describe('@feathersjs/express/rest provider', () => {

app.service('hook-status').hooks({
after (hook: HookContext) {
hook.http.statusCode = 206;
hook.http.status = 206;
}
});

Expand All @@ -205,6 +205,25 @@ describe('@feathersjs/express/rest provider', () => {
assert.strictEqual(res.status, 206);
});

it('allows to set response headers in a hook', async () => {
app.use('/hook-headers', {
async get () {
return {};
}
});

app.service('hook-headers').hooks({
after (hook: HookContext) {
hook.http.headers = { foo: 'first', bar: ['second', 'third'] };
}
});

const res = await axios.get<any>('http://localhost:4777/hook-headers/dishes');

assert.strictEqual(res.headers.foo, 'first');
assert.strictEqual(res.headers.bar, 'second, third');
});

it('sets the hook object in res.hook on error', async () => {
const params = {
route: {},
Expand Down
17 changes: 12 additions & 5 deletions packages/feathers/src/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,17 @@ export interface Params {

export interface Http {
/**
* A writeable, optional property that allows to override the standard HTTP status
* code that should be returned.
* A writeable, optional property with status code override.
*/
statusCode?: number;
status?: number;
/**
* A writeable, optional property with headers.
*/
headers?: { [key: string]: string | string[] };
/**
* A writeable, optional property with `Location` header's value.
*/
location?: string;
}

export interface HookContext<A = Application, S = any> extends BaseHookContext<ServiceGenericType<S>> {
Expand Down Expand Up @@ -333,11 +340,11 @@ export interface HookContext<A = Application, S = any> extends BaseHookContext<S
* A writeable, optional property that allows to override the standard HTTP status
* code that should be returned.
*
* @deprecated Use `http.statusCode` instead.
* @deprecated Use `http.status` instead.
*/
statusCode?: number;
/**
* A writeable, optional property that contains options specific to HTTP transports.
* A writeable, optional property with options specific to HTTP transports.
*/
http?: Http;
/**
Expand Down
4 changes: 2 additions & 2 deletions packages/feathers/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ export function hookMixin<A> (
event: null,
type: null,
get statusCode () {
return this.http?.statusCode;
return this.http?.status;
},
set statusCode (value: number) {
(this.http ||= {}).statusCode = value;
(this.http ||= {}).status = value;
}
});

Expand Down
11 changes: 5 additions & 6 deletions packages/koa/src/rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,13 @@ const serviceMiddleware = (): Middleware => {
const contextBase = createContext(service, method, { http: {} });
ctx.hook = contextBase;

const context = await (service as any)[method](...args, contextBase);
const context = await (service as any)[method](...args, contextBase);
ctx.hook = context;

const result = http.getData(context);
const statusCode = http.getStatusCode(context, result);

ctx.body = result;
ctx.status = statusCode;
const response = http.getResponse(context);
ctx.status = response.status;
ctx.set(response.headers);
ctx.body = response.body;

return next();
};
Expand Down
8 changes: 5 additions & 3 deletions packages/transport-commons/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@
"*.js"
],
"dependencies": {
"@feathersjs/commons": "^5.0.0-pre.17",
"@feathersjs/errors": "^5.0.0-pre.17",
"@feathersjs/feathers": "^5.0.0-pre.17",
"@feathersjs/commons": "^5.0.0-pre.16",
"@feathersjs/errors": "^5.0.0-pre.16",
"@feathersjs/feathers": "^5.0.0-pre.16",
"encodeurl": "^1.0.2",
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/encodeurl": "^1.0.0",
"@types/lodash": "^4.14.181",
"@types/mocha": "^9.1.0",
"@types/node": "^17.0.23",
Expand Down
41 changes: 30 additions & 11 deletions packages/transport-commons/src/http.ts
< B428 td class="blob-num blob-num-deletion empty-cell">
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { MethodNotAllowed } from '@feathersjs/errors/lib';
import { HookContext, NullableId, Params } from '@feathersjs/feathers';
import encodeUrl from 'encodeurl';

export const METHOD_HEADER = 'x-service-method';

Expand All @@ -13,7 +14,8 @@ export const statusCodes = {
created: 201,
noContent: 204,
methodNotAllowed: 405,
success: 200
success: 200,
seeOther: 303
};

export const knownMethods: { [key: string]: string } = {
Expand All @@ -25,7 +27,7 @@ export const knownMethods: { [key: string]: string } = {

export function getServiceMethod (_httpMethod: string, id: unknown, headerOverride?: string) {
const httpMethod = _httpMethod.toLowerCase();

if (httpMethod === 'post' && headerOverride) {
return headerOverride;
}
Expand Down Expand Up @@ -53,24 +55,41 @@ export const argumentsFor = {
default: ({ data, params }: ServiceParams) => [ data, params ]
}

export function getData (context: HookContext) {
return context.dispatch !== undefined
? context.dispatch
: context.result;
}
export function getStatusCode (context: HookContext, body: any, location: string|string[]) {
const { http = {} } = context;

export function getStatusCode (context: HookContext, data?: any) {
if (context.http?.statusCode) {
return context.http.statusCode;
if (http.status) {
return http.status;
}

if (context.method === 'create') {
return statusCodes.created;
}

if (!data) {
if (location !== undefined) {
return statusCodes.seeOther;
}

if (!body) {
return statusCodes.noContent;
}

return statusCodes.success;
}

export function getResponse (context: HookContext) {
const { http = {} } = context;
const body = context.dispatch !== undefined ? context.dispatch : context.result;

let headers = http.headers || {};
let location = headers.Location;

if (http.location !== undefined) {
location = encodeUrl(http.location);
headers = { ...headers, Location: location };
}

const status = getStatusCode(context, body, location);

return { status, headers, body };
}
37 changes: 28 additions & 9 deletions packages/transport-commons/test/http.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { HookContext } from '@feathersjs/feathers';
import { http } from '../src';

describe('@feathersjs/transport-commons HTTP helpers', () => {
it('getData', () => {
it('getResponse body', () => {
const plainData = { message: 'hi' };
const dispatch = { message: 'from dispatch' };
const resultContext = {
Expand All @@ -13,22 +13,41 @@ describe('@feathersjs/transport-commons HTTP helpers', () => {
dispatch
};

assert.deepStrictEqual(http.getData(resultContext as HookContext), plainData);
assert.deepStrictEqual(http.getData(dispatchContext as HookContext), dispatch);
assert.strictEqual(http.getResponse(resultContext as HookContext).body, plainData);
assert.strictEqual(http.getResponse(dispatchContext as HookContext).body, dispatch);
});

it('getStatusCode', async () => {
it('getResponse status', () => {
const statusContext = {
http: { statusCode: 202 }
http: { status: 202 }
};
const createContext = {
method: 'create'
};
const redirectContext = {
http: { location: '/' }
};

assert.strictEqual(http.getResponse(statusContext as HookContext).status, 202);
assert.strictEqual(http.getResponse(createContext as HookContext).status, http.statusCodes.created);
assert.strictEqual(http.getResponse(redirectContext as HookContext).status, http.statusCodes.seeOther);
assert.strictEqual(http.getResponse({} as HookContext).status, http.statusCodes.noContent);
assert.strictEqual(http.getResponse({result: true} as HookContext).status, http.statusCodes.success);
});

it('getResponse headers', () => {
const headers = { key: 'value' } as any;
const headersContext = {
http: { headers }
};
const locationContext = {
http: { location: '/' }
};

assert.strictEqual(http.getStatusCode(statusContext as HookContext, {}), 202);
assert.strictEqual(http.getStatusCode(createContext as HookContext, {}), http.statusCodes.created);
assert.strictEqual(http.getStatusCode({} as HookContext), http.statusCodes.noContent);
assert.strictEqual(http.getStatusCode({} as HookContext, {}), http.statusCodes.success);
assert.deepStrictEqual(http.getResponse({} as HookContext).headers, {});
assert.deepStrictEqual(http.getResponse({http: {}} as HookContext).headers, {});
assert.strictEqual(http.getResponse(headersContext as HookContext).headers, headers);
assert.deepStrictEqual(http.getResponse(locationContext as HookContext).headers, { Location: '/' });
});

it('getServiceMethod', () => {
Expand Down
0