Skip to content

Commit

Permalink
feat(express, koa): make transports similar (#2486)
Browse files Browse the repository at this point in the history
  • Loading branch information
vonagam authored Jan 9, 2022
1 parent 1093f12 commit 26aa937
Show file tree
Hide file tree
Showing 24 changed files with 401 additions and 336 deletions.
2 changes: 1 addition & 1 deletion packages/authentication/src/hooks/authenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const debug = createDebug('@feathersjs/authentication/hooks/authenticate');

export interface AuthenticateHookSettings {
service?: string;
strategies: string[];
strategies?: string[];
}

export default (originalSettings: string | AuthenticateHookSettings, ...originalStrategies: string[]) => {
Expand Down
11 changes: 6 additions & 5 deletions packages/client/test/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,18 @@ class TodoService extends Service {

export default (configurer?: (app: Application) => void) => {
const app = express.default(feathers())
// Parse HTTP bodies
.use(express.json())
.use(express.urlencoded({ extended: true }))
// Host the current directory (for index.html)
.use(express.static(process.cwd()))
.configure(express.rest());

if (typeof configurer === 'function') {
configurer.call(app, app);
}

// Parse HTTP bodies
app.use(express.json())
.use(express.urlencoded({ extended: true }))
// Host the current directory (for index.html)
.use(express.static(process.cwd()))
app
// Host our Todos service on the /todos path
.use('/todos', new TodoService({
multi: true
Expand Down
5 changes: 2 additions & 3 deletions packages/express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,16 @@
"access": "public"
},
"dependencies": {
"@feathersjs/authentication": "^5.0.0-pre.15",
"@feathersjs/commons": "^5.0.0-pre.15",
"@feathersjs/errors": "^5.0.0-pre.15",
"@feathersjs/feathers": "^5.0.0-pre.15",
"@feathersjs/transport-commons": "^5.0.0-pre.15",
"@types/express": "^4.17.13",
"@types/express-serve-static-core": "^4.17.27",
"express": "^4.17.2",
"lodash": "^4.17.21"
"express": "^4.17.2"
},
"devDependencies": {
"@feathersjs/authentication": "^5.0.0-pre.15",
"@feathersjs/authentication-local": "^5.0.0-pre.15",
"@feathersjs/tests": "^5.0.0-pre.15",
"@types/lodash": "^4.14.178",
Expand Down
82 changes: 36 additions & 46 deletions packages/express/src/authentication.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,61 @@
import { RequestHandler, Request, Response } from 'express';
import { HookContext } from '@feathersjs/feathers';
import { createDebug } from '@feathersjs/commons';
import { merge, flatten } from 'lodash';
import { NextFunction, RequestHandler, Request, Response } from 'express';
import { authenticate as AuthenticateHook } from '@feathersjs/authentication';

import { Application } from './declarations';

const debug = createDebug('@feathersjs/express/authentication');

type StrategyOptions = {
service?: string;
strategies: string[]
const toHandler = (func: (req: Request, res: Response, next: () => void) => Promise<void>): RequestHandler => {
return (req, res, next) => func(req, res, next).catch(error => next(error));
};

const normalizeStrategy = (_settings: string|StrategyOptions, ..._strategies: string[]) =>
typeof _settings === 'string'
? { strategies: flatten([ _settings, ..._strategies ]) }
: _settings;
export type AuthenticationSettings = {
service?: string;
strategies?: string[];
};

export function parseAuthentication (settings: any = {}): RequestHandler {
return function (req, res, next) {
const app = req.app as any;
const service = app.defaultAuthentication ? app.defaultAuthentication(settings.service) : null;
export function parseAuthentication (settings: AuthenticationSettings = {}): RequestHandler {
return toHandler(async (req, res, next) => {
const app = req.app as any as Application;
const service = app.defaultAuthentication?.(settings.service);

if (service === null) {
if (!service) {
return next();
}

const config = service.configuration;
const authStrategies = config.parseStrategies || config.authStrategies || [];
const authStrategies = settings.strategies || config.parseStrategies || config.authStrategies || [];

if (authStrategies.length === 0) {
debug('No `authStrategies` or `parseStrategies` found in authentication configuration');
return next();
}

service.parse(req, res, ...authStrategies)
.then((authentication: any) => {
if (authentication) {
debug('Parsed authentication from HTTP header', authentication);
merge(req, {
authentication,
feathers: { authentication }
});
}

next();
}).catch(next);
};
}
const authentication = await service.parse(req, res, ...authStrategies)

if (authentication) {
debug('Parsed authentication from HTTP header', authentication);
req.feathers = { ...req.feathers, authentication };
}

export function authenticate (_settings: string|StrategyOptions, ..._strategies: string[]) {
const settings = normalizeStrategy(_settings, ..._strategies);
return next();
});
}

if (!Array.isArray(settings.strategies) || settings.strategies.length === 0) {
throw new Error('\'authenticate\' middleware requires at least one strategy name');
}
export function authenticate (settings: string | AuthenticationSettings, ...strategies: string[]): RequestHandler {
const hook = AuthenticateHook(settings, ...strategies);

return (_req: Request, _res: Response, next: NextFunction) => {
const req = _req as any;
const { app, authentication } = req;
const service = app.defaultAuthentication(settings.service);
return toHandler(async (req, _res, next) => {
const app = req.app as any as Application;
const params = req.feathers;
const context = { app, params } as any as HookContext;

debug('Authenticating with Express middleware and strategies', settings.strategies);
await hook(context);

service.authenticate(authentication, req.feathers, ...settings.strategies)
.then((authResult: any) => {
debug('Merging request with', authResult);
merge(req, authResult);
req.feathers = context.params;

next();
}).catch(next);
};
return next();
});
}
28 changes: 15 additions & 13 deletions packages/express/src/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import http from 'http';
import express, { Express } from 'express';
import {
Application as FeathersApplication, Params as FeathersParams,
HookContext, ServiceMethods, ServiceInterface
HookContext, ServiceMethods, ServiceInterface, RouteLookup
} from '@feathersjs/feathers';

interface ExpressUseHandler<T, Services> {
Expand Down Expand Up @@ -33,28 +33,30 @@ export type Application<Services = any, Settings = any> =

declare module '@feathersjs/feathers/lib/declarations' {
interface ServiceOptions {
middleware?: {
before: express.RequestHandler[],
after: express.RequestHandler[]
}
express?: {
before?: express.RequestHandler[];
after?: express.RequestHandler[];
composed?: express.RequestHandler;
};
}
}

declare module 'express-serve-static-core' {
interface Request {
feathers?: Partial<FeathersParams>;
feathers?: Partial<FeathersParams>;
lookup?: RouteLookup;
}

interface Response {
data?: any;
hook?: HookContext;
data?: any;
hook?: HookContext;
}

interface IRouterMatcher<T> {
// eslint-disable-next-line
<P extends Params = ParamsDictionary, ResBody = any, ReqBody = any>(
path: PathParams,
...handlers: (RequestHandler<P, ResBody, ReqBody> | Partial<ServiceMethods> | Application)[]
): T;
// eslint-disable-next-line
<P extends Params = ParamsDictionary, ResBody = any, ReqBody = any>(
path: PathParams,
...handlers: (RequestHandler<P, ResBody, ReqBody> | Partial<ServiceMethods> | Application)[]
): T;
}
}
8 changes: 4 additions & 4 deletions packages/express/src/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ export function notFound ({ verbose = false } = {}): RequestHandler {
}

export type ErrorHandlerOptions = {
public?: string,
logger?: boolean|{ error?: (msg: any) => void, info?: (msg: any) => void },
html?: any,
json?: any
public?: string;
logger?: boolean|{ error?: (msg: any) => void, info?: (msg: any) => void };
html?: any;
json?: any;
};

export function errorHandler (_options: ErrorHandlerOptions = {}): ErrorRequestHandler {
Expand Down
65 changes: 35 additions & 30 deletions packages/express/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
import express, {
Express, static as _static, json, raw, text, urlencoded, query
} from 'express';
import {
Application as FeathersApplication, defaultServiceMethods
} from '@feathersjs/feathers';
import express, { Express } from 'express';
import { Application as FeathersApplication, defaultServiceMethods } from '@feathersjs/feathers';
import { routing } from '@feathersjs/transport-commons';
import { createDebug } from '@feathersjs/commons';

import { Application } from './declarations';
import { errorHandler, notFound } from './handlers';
import { parseAuthentication, authenticate } from './authentication';

export {
_static as serveStatic, _static as static, json, raw, text,
urlencoded, query, errorHandler, notFound, express as original,
authenticate, parseAuthentication
};
export { default as original, static, static as serveStatic, json, raw, text, urlencoded, query } from 'express';

export * from './rest';
export * from './authentication';
export * from './declarations';
export * from './handlers';
export * from './rest';

const debug = createDebug('@feathersjs/express');

Expand All @@ -30,10 +23,12 @@ export default function feathersExpress<S = any, C = any> (feathersApp?: Feather
throw new Error('@feathersjs/express requires a valid Feathers application instance');
}

const { use, listen } = expressApp as any;
// A mixin that provides the extended functionality
const mixin: any = {
use (location: string, ...rest: any[]) {
const app = expressApp as any as Application<S, C>;
const { use: expressUse, listen: expressListen } = expressApp as any;
const feathersUse = feathersApp.use;

Object.assign(app, {
use (location: string & keyof S, ...rest: any[]) {
let service: any;
let options = {};

Expand All @@ -60,46 +55,56 @@ export default function feathersExpress<S = any, C = any> (feathersApp?: Feather
// Check for service (any object with at least one service method)
if (hasMethod(['handle', 'set']) || !hasMethod(defaultServiceMethods)) {
debug('Passing app.use call to Express app');
return use.call(this, location, ...rest);
return expressUse.call(this, location, ...rest);
}

debug('Registering service with middleware', middleware);
// Since this is a service, call Feathers `.use`
(feathersApp as FeathersApplication).use.call(this, location, service, {
feathersUse.call(this, location, service, {
...options,
middleware
express: middleware
});

return this;
},

async listen (...args: any[]) {
const server = listen.call(this, ...args);
const server = expressListen.call(this, ...args);

await this.setup(server);
debug('Feathers application listening');

return server;
}
};
} as Application<S, C>);

const feathersDescriptors = {
const appDescriptors = {
...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(app)),
...Object.getOwnPropertyDescriptors(app)
};
const newDescriptors = {
...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(feathersApp)),
...Object.getOwnPropertyDescriptors(feathersApp)
};

// Copy all non-existing properties (including non-enumerables)
// that don't already exist on the Express app
Object.keys(feathersDescriptors).forEach(prop => {
const feathersProp = feathersDescriptors[prop];
const expressProp = Object.getOwnPropertyDescriptor(expressApp, prop);
Object.keys(newDescriptors).forEach(prop => {
const appProp = appDescriptors[prop];
const newProp = newDescriptors[prop];

if (expressProp === undefined && feathersProp !== undefined) {
Object.defineProperty(expressApp, prop, feathersProp);
if (appProp === undefined && newProp !== undefined) {
Object.defineProperty(expressApp, prop, newProp);
}
});

return Object.assign(expressApp, mixin);
app.configure(routing() as any);
app.use((req, _res, next) => {
req.feathers = { ...req.feathers, provider: 'rest' };
return next();
});

return app;
}

if (typeof module !== 'undefined') {
Expand Down
Loading

0 comments on commit 26aa937

Please sign in to comment.