From df28b7619161f1df5e700326f52cca1a92dc5d28 Mon Sep 17 00:00:00 2001 From: David Luecke Date: Wed, 20 Apr 2022 10:15:11 -0700 Subject: [PATCH] feat(typescript): Improve params and query typeability (#2600) --- packages/adapter-commons/src/service.ts | 11 +-- packages/authentication-oauth/src/strategy.ts | 6 +- packages/authentication-oauth/test/fixture.ts | 6 +- packages/authentication/src/core.ts | 12 +++- packages/authentication/src/index.ts | 1 + packages/authentication/src/jwt.ts | 4 +- packages/authentication/src/service.ts | 14 ++-- .../authentication/test/hooks/event.test.ts | 14 ++-- packages/express/src/declarations.ts | 4 +- packages/express/test/rest.test.ts | 7 +- packages/feathers/src/declarations.ts | 62 ++++++++-------- packages/feathers/test/events.test.ts | 15 ++-- packages/feathers/test/hooks/app.test.ts | 23 +++++- packages/feathers/test/hooks/async.test.ts | 48 +++++++++---- packages/feathers/test/hooks/before.test.ts | 71 +++++++++++++++---- packages/feathers/test/hooks/hooks.test.ts | 14 +++- packages/memory/src/index.ts | 2 +- packages/rest-client/src/axios.ts | 5 +- packages/rest-client/src/base.ts | 16 +++-- packages/rest-client/src/fetch.ts | 5 +- packages/rest-client/src/superagent.ts | 5 +- packages/rest-client/test/server.ts | 6 +- packages/schema/test/hooks.test.ts | 4 +- packages/socketio/src/middleware.ts | 2 +- 24 files changed, 234 insertions(+), 123 deletions(-) diff --git a/packages/adapter-commons/src/service.ts b/packages/adapter-commons/src/service.ts index 349592f784..2a7dc973df 100644 --- a/packages/adapter-commons/src/service.ts +++ b/packages/adapter-commons/src/service.ts @@ -16,14 +16,16 @@ const alwaysMulti: { [key: string]: boolean } = { update: false }; +export interface PaginationOptions { + default?: number; + max?: number; +} + export interface ServiceOptions { events?: string[]; multi?: boolean|string[]; id?: string; - paginate?: { - default?: number; - max?: number; - } + paginate?: PaginationOptions /** * @deprecated renamed to `allow`. */ @@ -38,6 +40,7 @@ export interface AdapterOptions extends Pick extends Params { adapter?: Partial>; + paginate?: false|PaginationOptions; } /** diff --git a/packages/authentication-oauth/src/strategy.ts b/packages/authentication-oauth/src/strategy.ts index 108eab585f..1ba0baae25 100644 --- a/packages/authentication-oauth/src/strategy.ts +++ b/packages/authentication-oauth/src/strategy.ts @@ -2,7 +2,7 @@ // @ts-ignore import querystring from 'querystring'; import { - AuthenticationRequest, AuthenticationBaseStrategy, AuthenticationResult + AuthenticationRequest, AuthenticationBaseStrategy, AuthenticationResult, AuthenticationParams } from '@feathersjs/authentication'; import { Params } from '@feathersjs/feathers'; import { NotAuthenticated } from '@feathersjs/errors'; @@ -84,7 +84,7 @@ export class OAuthStrategy extends AuthenticationBaseStrategy { return redirect; } - async getRedirect (data: AuthenticationResult|Error, params?: Params): Promise { + async getRedirect (data: AuthenticationResult|Error, params?: AuthenticationParams): Promise { const queryRedirect = (params && params.redirect) || ''; const redirect = await this.getAllowedOrigin(params); @@ -156,7 +156,7 @@ export class OAuthStrategy extends AuthenticationBaseStrategy { }); } - async authenticate (authentication: AuthenticationRequest, originalParams: Params) { + async authenticate (authentication: AuthenticationRequest, originalParams: AuthenticationParams) { const entity: string = this.configuration.entity; const { provider, ...params } = originalParams; const profile = await this.getProfile(authentication, params); diff --git a/packages/authentication-oauth/test/fixture.ts b/packages/authentication-oauth/test/fixture.ts index 1dc58da77a..f58cbeaffa 100644 --- a/packages/authentication-oauth/test/fixture.ts +++ b/packages/authentication-oauth/test/fixture.ts @@ -1,11 +1,11 @@ -import { feathers, Params } from '@feathersjs/feathers'; +import { feathers } from '@feathersjs/feathers'; import express, { rest, errorHandler } from '@feathersjs/express'; import { memory } from '@feathersjs/memory'; -import { AuthenticationService, JWTStrategy, AuthenticationRequest } from '@feathersjs/authentication'; +import { AuthenticationService, JWTStrategy, AuthenticationRequest, AuthenticationParams } from '@feathersjs/authentication'; import { express as oauth, OAuthStrategy } from '../src'; export class TestOAuthStrategy extends OAuthStrategy { - async authenticate (data: AuthenticationRequest, params: Params) { + async authenticate (data: AuthenticationRequest, params: AuthenticationParams) { const { fromMiddleware } = params; const authResult = await super.authenticate(data, params); diff --git a/packages/authentication/src/core.ts b/packages/authentication/src/core.ts index d5e5dcc3b1..4c0f399e12 100644 --- a/packages/authentication/src/core.ts +++ b/packages/authentication/src/core.ts @@ -18,6 +18,14 @@ export interface AuthenticationRequest { [key: string]: any; } +export interface AuthenticationParams extends Params { + payload?: { [key: string]: any }; + jwtOptions?: SignOptions; + authStrategies?: string[]; + secret?: string; + [key: string]: any; +} + export type ConnectionEvent = 'login' | 'logout' | 'disconnect'; export interface AuthenticationStrategy { @@ -57,7 +65,7 @@ export interface AuthenticationStrategy { * @param authentication The authentication request * @param params The service call parameters */ - authenticate? (authentication: AuthenticationRequest, params: Params): Promise; + authenticate? (authentication: AuthenticationRequest, params: AuthenticationParams): Promise; /** * Update a real-time connection according to this strategy. * @@ -232,7 +240,7 @@ export class AuthenticationBase { * @param params Service call parameters * @param allowed A list of allowed strategy names */ - async authenticate (authentication: AuthenticationRequest, params: Params, ...allowed: string[]) { + async authenticate (authentication: AuthenticationRequest, params: AuthenticationParams, ...allowed: string[]) { const { strategy } = authentication || {}; const [ authStrategy ] = this.getStrategies(strategy); const strategyAllowed = allowed.includes(strategy); diff --git a/packages/authentication/src/index.ts b/packages/authentication/src/index.ts index 5ec3d7349c..2ec48804a4 100644 --- a/packages/authentication/src/index.ts +++ b/packages/authentication/src/index.ts @@ -5,6 +5,7 @@ export { AuthenticationRequest, AuthenticationResult, AuthenticationStrategy, + AuthenticationParams, ConnectionEvent } from './core'; export { AuthenticationBaseStrategy } from './strategy'; diff --git a/packages/authentication/src/jwt.ts b/packages/authentication/src/jwt.ts index 910e41190b..f31d76025f 100644 --- a/packages/authentication/src/jwt.ts +++ b/packages/authentication/src/jwt.ts @@ -8,7 +8,7 @@ import { createDebug } from '@feathersjs/commons'; import lt from 'long-timeout'; import { AuthenticationBaseStrategy } from './strategy'; -import { AuthenticationRequest, AuthenticationResult, ConnectionEvent } from './core'; +import { AuthenticationParams, AuthenticationRequest, AuthenticationResult, ConnectionEvent } from './core'; const debug = createDebug('@feathersjs/authentication/jwt'); const SPLIT_HEADER = /(\S+)\s+(\S+)/; @@ -116,7 +116,7 @@ export class JWTStrategy extends AuthenticationBaseStrategy { return authResult.authentication.payload.sub; } - async authenticate (authentication: AuthenticationRequest, params: Params) { + async authenticate (authentication: AuthenticationRequest, params: AuthenticationParams) { const { accessToken } = authentication; const { entity } = this.configuration; diff --git a/packages/authentication/src/service.ts b/packages/authentication/src/service.ts index 627a4acbd2..41241786f0 100644 --- a/packages/authentication/src/service.ts +++ b/packages/authentication/src/service.ts @@ -1,10 +1,10 @@ import merge from 'lodash/merge'; import { NotAuthenticated } from '@feathersjs/errors'; -import { AuthenticationBase, AuthenticationResult, AuthenticationRequest } from './core'; +import { AuthenticationBase, AuthenticationResult, AuthenticationRequest, AuthenticationParams } from './core'; import { connection, event } from './hooks'; import '@feathersjs/transport-commons'; import { createDebug } from '@feathersjs/commons'; -import { Params, ServiceMethods, ServiceAddons } from '@feathersjs/feathers'; +import { ServiceMethods, ServiceAddons } from '@feathersjs/feathers'; import jsonwebtoken from 'jsonwebtoken'; const debug = createDebug('@feathersjs/authentication/service'); @@ -29,7 +29,7 @@ declare module '@feathersjs/feathers/lib/declarations' { // eslint-disable-next-line export interface AuthenticationService extends ServiceAddons {} -export class AuthenticationService extends AuthenticationBase implements Partial> { +export class AuthenticationService extends AuthenticationBase implements Partial> { constructor (app: any, configKey = 'authentication', options = {}) { super(app, configKey, options); @@ -51,7 +51,7 @@ export class AuthenticationService extends AuthenticationBase implements Partial * @param _authResult The current authentication result * @param params The service call parameters */ - async getPayload (_authResult: AuthenticationResult, params: Params) { + async getPayload (_authResult: AuthenticationResult, params: AuthenticationParams) { // Uses `params.payload` or returns an empty payload const { payload = {} } = params; @@ -65,7 +65,7 @@ export class AuthenticationService extends AuthenticationBase implements Partial * @param authResult The authentication result * @param params Service call parameters */ - async getTokenOptions (authResult: AuthenticationResult, params: Params) { + async getTokenOptions (authResult: AuthenticationResult, params: AuthenticationParams) { const { service, entity, entityId } = this.configuration; const jwtOptions = merge({}, params.jwtOptions, params.jwt); const value = service && entity && authResult[entity]; @@ -92,7 +92,7 @@ export class AuthenticationService extends AuthenticationBase implements Partial * @param data The authentication request (should include `strategy` key) * @param params Service call parameters */ - async create (data: AuthenticationRequest, params?: Params) { + async create (data: AuthenticationRequest, params?: AuthenticationParams) { const authStrategies = params.authStrategies || this.configuration.authStrategies; if (!authStrategies.length) { @@ -131,7 +131,7 @@ export class AuthenticationService extends AuthenticationBase implements Partial * @param id The JWT to remove or null * @param params Service call parameters */ - async remove (id: string | null, params?: Params) { + async remove (id: string | null, params?: AuthenticationParams) { const { authentication } = params; const { authStrategies } = this.configuration; diff --git a/packages/authentication/test/hooks/event.test.ts b/packages/authentication/test/hooks/event.test.ts index 817be37102..4daffcaa9e 100644 --- a/packages/authentication/test/hooks/event.test.ts +++ b/packages/authentication/test/hooks/event.test.ts @@ -1,11 +1,11 @@ import assert from 'assert'; -import { feathers, Params, HookContext } from '@feathersjs/feathers'; +import { feathers, HookContext } from '@feathersjs/feathers'; import hook from '../../src/hooks/event'; -import { AuthenticationRequest, AuthenticationResult } from '../../src/core'; +import { AuthenticationParams, AuthenticationRequest, AuthenticationResult } from '../../src/core'; describe('authentication/hooks/events', () => { - const app = feathers().use('/authentication', { + const app = feathers().use('authentication', { async create (data: AuthenticationRequest) { return data; }, @@ -27,7 +27,7 @@ describe('authentication/hooks/events', () => { message: 'test' }; - app.once('login', (result: AuthenticationResult, params: Params, context: HookContext) => { + app.once('login', (result: AuthenticationResult, params: AuthenticationParams, context: HookContext) => { try { assert.deepStrictEqual(result, data); assert.ok(params.testParam); @@ -41,11 +41,11 @@ describe('authentication/hooks/events', () => { service.create(data, { testParam: true, provider: 'test' - }); + }as any); }); it('logout', done => { - app.once('logout', (result: AuthenticationResult, params: Params, context: HookContext) => { + app.once('logout', (result: AuthenticationResult, params: AuthenticationParams, context: HookContext) => { try { assert.deepStrictEqual(result, { id: 'test' @@ -61,7 +61,7 @@ describe('authentication/hooks/events', () => { service.remove('test', { testParam: true, provider: 'test' - }); + } as any); }); it('does nothing when provider is not set', done => { diff --git a/packages/express/src/declarations.ts b/packages/express/src/declarations.ts index aaf30b2dbd..8672afc7cd 100644 --- a/packages/express/src/declarations.ts +++ b/packages/express/src/declarations.ts @@ -9,7 +9,7 @@ interface ExpressUseHandler { ( path: L, ...middlewareOrService: ( - Express|express.RequestHandler| + Express|express.RequestHandler|express.RequestHandler[]| (keyof any extends keyof Services ? ServiceInterface : Services[L]) )[] ): T; @@ -44,7 +44,7 @@ declare module '@feathersjs/feathers/lib/declarations' { declare module 'express-serve-static-core' { interface Request { - feathers?: Partial; + feathers?: Partial & { [key: string]: any }; lookup?: RouteLookup; } diff --git a/packages/express/test/rest.test.ts b/packages/express/test/rest.test.ts index 842e14f693..f774c31f4e 100644 --- a/packages/express/test/rest.test.ts +++ b/packages/express/test/rest.test.ts @@ -380,10 +380,9 @@ describe('@feathersjs/express/rest provider', () => { it('allows middleware arrays before and after a service', async () => { const app = expressify(feathers()); - app - .use(express.json()) - .configure(rest()) - .use('/todo', [function (req: Request, _res: Response, next: NextFunction) { + app.use(express.json()); + app.configure(rest()); + app.use('/todo', [function (req: Request, _res: Response, next: NextFunction) { req.body.before = ['before first']; next(); }, function (req: Request, _res: Response, next: NextFunction) { diff --git a/packages/feathers/src/declarations.ts b/packages/feathers/src/declarations.ts index ec883896d1..ad7a90fc4b 100644 --- a/packages/feathers/src/declarations.ts +++ b/packages/feathers/src/declarations.ts @@ -21,87 +21,87 @@ export interface ServiceOptions { routeParams?: { [key: string]: any }; } -export interface ServiceMethods> { - find (params?: Params): Promise; +export interface ServiceMethods, P = Params> { + find (params?: P): Promise; - get (id: Id, params?: Params): Promise; + get (id: Id, params?: P): Promise; - create (data: D, params?: Params): Promise; + create (data: D, params?: P): Promise; - update (id: NullableId, data: D, params?: Params): Promise; + update (id: NullableId, data: D, params?: P): Promise; - patch (id: NullableId, data: D, params?: Params): Promise; + patch (id: NullableId, data: D, params?: P): Promise; - remove (id: NullableId, params?: Params): Promise; + remove (id: NullableId, params?: P): Promise; setup (app: Application, path: string): Promise; teardown (app: Application, path: string): Promise; } -export interface ServiceOverloads> { - create? (data: D[], params?: Params): Promise; +export interface ServiceOverloads, P = Params> { + create? (data: D[], params?: P): Promise; - update? (id: Id, data: D, params?: Params): Promise; + update? (id: Id, data: D, params?: P): Promise; - update? (id: null, data: D, params?: Params): Promise; + update? (id: null, data: D, params?: P): Promise; - patch? (id: Id, data: D, params?: Params): Promise; + patch? (id: Id, data: D, params?: P): Promise; - patch? (id: null, data: D, params?: Params): Promise; + patch? (id: null, data: D, params?: P): Promise; - remove? (id: Id, params?: Params): Promise; + remove? (id: Id, params?: P): Promise; - remove? (id: null, params?: Params): Promise; + remove? (id: null, params?: P): Promise; } -export type Service> = - ServiceMethods & - ServiceOverloads; +export type Service, P = Params> = + ServiceMethods & + ServiceOverloads; -export type ServiceInterface> = - Partial>; +export type ServiceInterface, P = Params> = + Partial>; export interface ServiceAddons extends EventEmitter { id?: string; hooks (options: HookOptions): this; } -export interface ServiceHookOverloads { +export interface ServiceHookOverloads { find ( - params: Params, + params: P, context: HookContext ): Promise; get ( id: Id, - params: Params, + params: P, context: HookContext ): Promise; create ( data: ServiceGenericData | ServiceGenericData[], - params: Params, + params: P, context: HookContext ): Promise; update ( id: NullableId, data: ServiceGenericData, - params: Params, + params: P, context: HookContext ): Promise; patch ( id: NullableId, data: ServiceGenericData, - params: Params, + params: P, context: HookContext ): Promise; remove ( id: NullableId, - params: Params, + params: P, context: HookContext ): Promise; } @@ -117,6 +117,7 @@ export type ServiceMixin = (service: FeathersService, path: string, option export type ServiceGenericType = S extends ServiceInterface ? T : any; export type ServiceGenericData = S extends ServiceInterface ? D : any; +export type ServiceGenericParams = S extends ServiceInterface ? P : any; export interface FeathersApplication { /** @@ -246,12 +247,11 @@ export interface Query { [key: string]: any; } -export interface Params { - query?: Query; +export interface Params { + query?: Q; provider?: string; route?: { [key: string]: any }; headers?: { [key: string]: any }; - [key: string]: any; // (JL) not sure if we want this } export interface Http { @@ -319,7 +319,7 @@ export interface HookContext extends BaseHookContext; /** * A writeable property containing the result of the successful service method call. * It is only available in after hooks. diff --git a/packages/feathers/test/events.test.ts b/packages/feathers/test/events.test.ts index 1b6e5def09..a8c383eaac 100644 --- a/packages/feathers/test/events.test.ts +++ b/packages/feathers/test/events.test.ts @@ -236,10 +236,15 @@ describe('Service events', () => { }); it('.remove and removed with array', async () => { + const removeItems = [ + { message: 'Hello 0' }, + { message: 'Hello 1' } + ]; + const app = feathers().use('/creator', { async remove (id: any, data: any) { - if (Array.isArray(data)) { - return Promise.all(data.map((current, index) => + if (id === null) { + return Promise.all(removeItems.map((current, index) => (this as any).remove(index, current)) ); } @@ -248,10 +253,6 @@ describe('Service events', () => { }); const service = app.service('creator'); - const removeItems = [ - { message: 'Hello 0' }, - { message: 'Hello 1' } - ]; const events = Promise.all(removeItems.map((element, index) => { return new Promise((resolve) => { @@ -264,7 +265,7 @@ describe('Service events', () => { }); })); - await service.remove(null, removeItems); + await service.remove(null); await events; }); }); diff --git a/packages/feathers/test/hooks/app.test.ts b/packages/feathers/test/hooks/app.test.ts index 96c51473bd..adcd0c9603 100644 --- a/packages/feathers/test/hooks/app.test.ts +++ b/packages/feathers/test/hooks/app.test.ts @@ -1,12 +1,29 @@ import assert from 'assert'; -import { feathers, Application, ApplicationHookMap } from '../../src'; +import { feathers, Application, ApplicationHookMap, ServiceInterface, Params } from '../../src'; + +type Todo = { + id?: string, + params?: TodoParams, + data?: any, + test?: string, + order?: string[] +} + +interface TodoParams extends Params { + order: string[]; + ran: boolean; +} + +type TodoService = ServiceInterface; + +type App = Application<{ todos: TodoService }>; describe('app.hooks', () => { - let app: Application; + let app: App; beforeEach(() => { - app = feathers().use('/todos', { + app = feathers().use('todos', { async get (id: any, params: any) { if (id === 'error') { throw new Error('Something went wrong'); diff --git a/packages/feathers/test/hooks/async.test.ts b/packages/feathers/test/hooks/async.test.ts index 90e28ffaab..07795867c4 100644 --- a/packages/feathers/test/hooks/async.test.ts +++ b/packages/feathers/test/hooks/async.test.ts @@ -1,5 +1,5 @@ import assert from 'assert'; -import { feathers } from '../../src'; +import { feathers, Params, ServiceInterface } from '../../src'; describe('`async` hooks', () => { it('async hooks can set hook.result which will skip service method', async () => { @@ -137,8 +137,12 @@ describe('`async` hooks', () => { }); it('adds .hooks() and chains multiple hooks for the same method', async () => { - const dummyService = { - create (data: any, params: any) { + interface DummyParams extends Params { + modified: string + } + + class DummyService implements ServiceInterface { + create (data: any, params?: any) { assert.deepStrictEqual(data, { some: 'thing', modified: 'second data' @@ -150,8 +154,9 @@ describe('`async` hooks', () => { return Promise.resolve(data); } - }; - const app = feathers().use('/dummy', dummyService); + } + + const app = feathers<{ dummy: DummyService }>().use('dummy', new DummyService()); const service = app.service('dummy'); service.hooks({ @@ -170,16 +175,22 @@ describe('`async` hooks', () => { }); it('async hooks run in the correct order', async () => { - const app = feathers().use('/dummy', { - async get (id: any, params: any) { + interface DummyParams extends Params<{ name: string }> { + items: string[] + } + + class DummyService implements ServiceInterface { + async get (id: any, params?: DummyParams) { assert.deepStrictEqual(params.items, ['first', 'second', 'third']); return { id, - items: [] + items: [] as string[] }; } - }); + } + + const app = feathers<{ dummy: DummyService }>().use('dummy', new DummyService()); const service = app.service('dummy'); service.hooks({ @@ -206,7 +217,14 @@ describe('`async` hooks', () => { }); it('async all hooks (#11)', async () => { - const app = feathers().use('/dummy', { + interface DummyParams extends Params { + asyncAllObject: boolean; + asyncAllMethodArray: boolean; + } + + type DummyService = ServiceInterface; + + const app = feathers<{ dummy: DummyService }>().use('dummy', { async get (id: any, params: any) { assert.ok(params.asyncAllObject); assert.ok(params.asyncAllMethodArray); @@ -245,10 +263,14 @@ describe('`async` hooks', () => { }); it('async hooks have service as context and keep it in service method (#17)', async () => { - class Dummy { + interface DummyParams extends Params { + test: number; + } + + class Dummy implements ServiceInterface { number= 42; - async get (id: any, params: any) { + async get (id: any, params?: DummyParams) { return { id, number: (this as any).number, @@ -257,7 +279,7 @@ describe('`async` hooks', () => { } } - const app = feathers().use('/dummy', new Dummy()); + const app = feathers<{ dummy: Dummy }>().use('dummy', new Dummy()); const service = app.service('dummy'); diff --git a/packages/feathers/test/hooks/before.test.ts b/packages/feathers/test/hooks/before.test.ts index 8042656208..4443341375 100644 --- a/packages/feathers/test/hooks/before.test.ts +++ b/packages/feathers/test/hooks/before.test.ts @@ -1,10 +1,16 @@ import assert from 'assert'; -import { feathers } from '../../src'; +import { feathers, Params, ServiceInterface } from '../../src'; describe('`before` hooks', () => { it('.before hooks can return a promise', async () => { - const app = feathers().use('/dummy', { - async get (id: any, params: any) { + interface DummyParams extends Params { + ran: boolean; + } + + type DummyService = ServiceInterface; + + const app = feathers<{ dummy: DummyService }>().use('dummy', { + async get (id: any, params: DummyParams) { assert.ok(params.ran, 'Ran through promise hook'); return { @@ -43,7 +49,13 @@ describe('`before` hooks', () => { }); it('.before hooks do not need to return anything', async () => { - const app = feathers().use('/dummy', { + interface DummyParams extends Params { + ran: boolean; + } + + type DummyService = ServiceInterface; + + const app = feathers<{ dummy: DummyService }>().use('dummy', { async get (id: any, params: any) { assert.ok(params.ran, 'Ran through promise hook'); @@ -198,6 +210,12 @@ describe('`before` hooks', () => { }); it('calling back with no arguments uses the old ones', async () => { + interface DummyParams extends Params { + my: string; + } + + type DummyService = ServiceInterface; + const dummyService = { async remove (id: any, params: any) { assert.strictEqual(id, 1, 'Got id'); @@ -206,7 +224,7 @@ describe('`before` hooks', () => { return { id }; } }; - const app = feathers().use('/dummy', dummyService); + const app = feathers<{ dummy: DummyService }>().use('dummy', dummyService); const service = app.service('dummy'); service.hooks({ @@ -221,6 +239,12 @@ describe('`before` hooks', () => { }); it('adds .hooks() and chains multiple hooks for the same method', async () => { + interface DummyParams extends Params { + modified: string; + } + + type DummyService = ServiceInterface; + const dummyService = { async create (data: any, params: any) { assert.deepStrictEqual(data, { @@ -235,7 +259,7 @@ describe('`before` hooks', () => { return data; } }; - const app = feathers().use('/dummy', dummyService); + const app = feathers<{ dummy: DummyService }>().use('dummy', dummyService); const service = app.service('dummy'); service.hooks({ @@ -262,6 +286,12 @@ describe('`before` hooks', () => { }); it('chains multiple before hooks using array syntax', async () => { + interface DummyParams extends Params { + modified: string; + } + + type DummyService = ServiceInterface; + const dummyService = { async create (data: any, params: any) { assert.deepStrictEqual(data, { @@ -277,7 +307,7 @@ describe('`before` hooks', () => { } }; - const app = feathers().use('/dummy', dummyService); + const app = feathers<{ dummy: DummyService }>().use('dummy', dummyService); const service = app.service('dummy'); service.hooks({ @@ -301,7 +331,13 @@ describe('`before` hooks', () => { }); it('.before hooks run in the correct order (#13)', async () => { - const app = feathers().use('/dummy', { + interface DummyParams extends Params { + items: string[]; + } + + type DummyService = ServiceInterface; + + const app = feathers<{ dummy: DummyService }>().use('dummy', { async get (id: any, params: any) { assert.deepStrictEqual(params.items, ['first', 'second', 'third']); @@ -344,7 +380,14 @@ describe('`before` hooks', () => { }); it('before all hooks (#11)', async () => { - const app = feathers().use('/dummy', { + interface DummyParams extends Params { + beforeAllObject: boolean; + beforeAllMethodArray: boolean; + } + + type DummyService = ServiceInterface; + + const app = feathers<{ dummy: DummyService }>().use('dummy', { async get (id: any, params: any) { assert.ok(params.beforeAllObject); assert.ok(params.beforeAllMethodArray); @@ -389,10 +432,14 @@ describe('`before` hooks', () => { }); it('before hooks have service as context and keep it in service method (#17)', async () => { - class Dummy { + interface DummyParams extends Params { + test: number; + } + + class Dummy implements ServiceInterface { number = 42; - async get (id: any, params: any) { + async get (id: any, params?: DummyParams) { return { id, number: this.number, @@ -401,7 +448,7 @@ describe('`before` hooks', () => { } } - const app = feathers().use('/dummy', new Dummy()); + const app = feathers<{ dummy: Dummy }>().use('dummy', new Dummy()); const service = app.service('dummy'); service.hooks({ diff --git a/packages/feathers/test/hooks/hooks.test.ts b/packages/feathers/test/hooks/hooks.test.ts index f463c6feab..aeca6724e7 100644 --- a/packages/feathers/test/hooks/hooks.test.ts +++ b/packages/feathers/test/hooks/hooks.test.ts @@ -1,11 +1,15 @@ import assert from 'assert'; import { hooks, NextFunction } from '@feathersjs/hooks'; -import { HookContext, createContext, feathers, Id, Params } from '../../src'; +import { HookContext, createContext, feathers, Id, Params, ServiceInterface } from '../../src'; describe('hooks basics', () => { it('mix @feathersjs/hooks and .hooks', async () => { + interface SimpleParams extends Params { + chain: string[]; + + } class SimpleService { - async get (id: Id, params: Params) { + async get (id: Id, params: SimpleParams) { return { id, chain: params.chain }; } } @@ -121,7 +125,11 @@ describe('hooks basics', () => { // }); it('works with services that return a promise (feathers-hooks#28)', async () => { - const app = feathers().use('/dummy', { + interface DummyParams extends Params { + user: string; + } + + const app = feathers<{ dummy: ServiceInterface }>().use('dummy', { async get (id: any, params: any) { return { id, user: params.user }; } diff --git a/packages/memory/src/index.ts b/packages/memory/src/index.ts index 061c00b6c3..7c4ab0f12d 100644 --- a/packages/memory/src/index.ts +++ b/packages/memory/src/index.ts @@ -22,7 +22,7 @@ const _select = (data: any, params: any, ...args: any[]) => { }; export class Service> extends AdapterService implements InternalServiceMethods { - options: MemoryServiceOptions; + options: MemoryServiceOptions; store: MemoryServiceStore; _uId: number; diff --git a/packages/rest-client/src/axios.ts b/packages/rest-client/src/axios.ts index e90b8b76f4..5c589c1be7 100644 --- a/packages/rest-client/src/axios.ts +++ b/packages/rest-client/src/axios.ts @@ -1,8 +1,7 @@ -import { Params } from '@feathersjs/feathers'; -import { Base } from './base'; +import { Base, RestClientParams } from './base'; export class AxiosClient extends Base { - request (options: any, params: Params) { + request (options: any, params: RestClientParams) { const config = Object.assign({ url: options.url, method: options.method, diff --git a/packages/rest-client/src/base.ts b/packages/rest-client/src/base.ts index 6ea113e1a7..9fcff1c081 100644 --- a/packages/rest-client/src/base.ts +++ b/packages/rest-client/src/base.ts @@ -11,6 +11,10 @@ function toError (error: Error & { code: string }) { throw convert(error); } +export interface RestClientParams extends Params { + connection?: any; +} + interface RestClientSettings { name: string; base: string; @@ -73,7 +77,7 @@ export abstract class Base> implements ServiceInterface< return this; } - find (params: Params = {}) { + find (params: RestClientParams = {}) { return this.request({ url: this.makeUrl(params.query), method: 'GET', @@ -81,7 +85,7 @@ export abstract class Base> implements ServiceInterface< }, params).catch(toError); } - get (id: Id, params: Params = {}) { + get (id: Id, params: RestClientParams = {}) { if (typeof id === 'undefined') { return Promise.reject(new Error('id for \'get\' can not be undefined')); } @@ -93,7 +97,7 @@ export abstract class Base> implements ServiceInterface< }, params).catch(toError); } - create (body: D, params: Params = {}) { + create (body: D, params: RestClientParams = {}) { return this.request({ url: this.makeUrl(params.query), body, @@ -102,7 +106,7 @@ export abstract class Base> implements ServiceInterface< }, params).catch(toError); } - update (id: NullableId, body: D, params: Params = {}) { + update (id: NullableId, body: D, params: RestClientParams = {}) { if (typeof id === 'undefined') { return Promise.reject(new Error('id for \'update\' can not be undefined, only \'null\' when updating multiple entries')); } @@ -115,7 +119,7 @@ export abstract class Base> implements ServiceInterface< }, params).catch(toError); } - patch (id: NullableId, body: D, params: Params = {}) { + patch (id: NullableId, body: D, params: RestClientParams = {}) { if (typeof id === 'undefined') { return Promise.reject(new Error('id for \'patch\' can not be undefined, only \'null\' when updating multiple entries')); } @@ -128,7 +132,7 @@ export abstract class Base> implements ServiceInterface< }, params).catch(toError); } - remove (id: NullableId, params: Params = {}) { + remove (id: NullableId, params: RestClientParams = {}) { if (typeof id === 'undefined') { return Promise.reject(new Error('id for \'remove\' can not be undefined, only \'null\' when removing multiple entries')); } diff --git a/packages/rest-client/src/fetch.ts b/packages/rest-client/src/fetch.ts index 5e0703974c..d7362f3102 100644 --- a/packages/rest-client/src/fetch.ts +++ b/packages/rest-client/src/fetch.ts @@ -1,9 +1,8 @@ -import { Params } from '@feathersjs/feathers'; import { errors } from '@feathersjs/errors'; -import { Base } from './base'; +import { Base, RestClientParams } from './base'; export class FetchClient extends Base { - request (options: any, params: Params) { + request (options: any, params: RestClientParams) { const fetchOptions = Object.assign({}, options, params.connection); fetchOptions.headers = Object.assign({ diff --git a/packages/rest-client/src/superagent.ts b/packages/rest-client/src/superagent.ts index 7666a16dd3..c61744aa1e 100644 --- a/packages/rest-client/src/superagent.ts +++ b/packages/rest-client/src/superagent.ts @@ -1,8 +1,7 @@ -import { Params } from '@feathersjs/feathers'; -import { Base } from './base'; +import { Base, RestClientParams } from './base'; export class SuperagentClient extends Base { - request (options: any, params: Params) { + request (options: any, params: RestClientParams) { const superagent = this.connection(options.method, options.url) .set(this.options.headers || {}) .set('Accept', 'application/json') diff --git a/packages/rest-client/test/server.ts b/packages/rest-client/test/server.ts index c738499612..d7ca93150e 100644 --- a/packages/rest-client/test/server.ts +++ b/packages/rest-client/test/server.ts @@ -29,9 +29,13 @@ const errorHandler = function (error: FeathersError, _req: any, res: any, _next: }); }; +interface TodoServiceParams extends Params { + authorization: any; +} + // Create an in-memory CRUD service for our Todos class TodoService extends Service { - get (id: Id, params: Params) { + get (id: Id, params: TodoServiceParams) { if (params.query.error) { throw new Error('Something went wrong'); } diff --git a/packages/schema/test/hooks.test.ts b/packages/schema/test/hooks.test.ts index 62e7620b17..338a3fb331 100644 --- a/packages/schema/test/hooks.test.ts +++ b/packages/schema/test/hooks.test.ts @@ -58,7 +58,7 @@ describe('@feathersjs/schema/hooks', () => { await assert.rejects(() => app.service('messages').find({ provider: 'external', error: true - }), { + } as any), { name: 'BadRequest', message: 'Error resolving data', code: 400, @@ -153,7 +153,7 @@ describe('@feathersjs/schema/hooks', () => { const userMessages = await app.service('messages').find({ user - }) as MessageResult[]; + } as any) as MessageResult[]; assert.strictEqual(userMessages.length, 1); assert.strictEqual(userMessages[0].userId, user.id); diff --git a/packages/socketio/src/middleware.ts b/packages/socketio/src/middleware.ts index c4e0d21c4c..fd960e8574 100644 --- a/packages/socketio/src/middleware.ts +++ b/packages/socketio/src/middleware.ts @@ -7,7 +7,7 @@ const debug = createDebug('@feathersjs/socketio/middleware'); export type ParamsGetter = (socket: Socket) => any; export type NextFunction = (err?: any) => void; export interface FeathersSocket extends Socket { - feathers?: Params + feathers?: Params & { [key: string]: any } } export const disconnect = (app: Application, getParams: ParamsGetter) =>