@feathersjs/transport-commons 5.0.0-pre.2 → 5.0.0-pre.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/http.ts ADDED
@@ -0,0 +1,95 @@
1
+ import { MethodNotAllowed } from '@feathersjs/errors/lib';
2
+ import { HookContext, NullableId, Params } from '@feathersjs/feathers';
3
+ import encodeUrl from 'encodeurl';
4
+
5
+ export const METHOD_HEADER = 'x-service-method';
6
+
7
+ export interface ServiceParams {
8
+ id: NullableId,
9
+ data: any,
10
+ params: Params
11
+ }
12
+
13
+ export const statusCodes = {
14
+ created: 201,
15
+ noContent: 204,
16
+ methodNotAllowed: 405,
17
+ success: 200,
18
+ seeOther: 303
19
+ };
20
+
21
+ export const knownMethods: { [key: string]: string } = {
22
+ post: 'create',
23
+ patch: 'patch',
24
+ put: 'update',
25
+ delete: 'remove'
26
+ };
27
+
28
+ export function getServiceMethod (_httpMethod: string, id: unknown, headerOverride?: string) {
29
+ const httpMethod = _httpMethod.toLowerCase();
30
+
31
+ if (httpMethod === 'post' && headerOverride) {
32
+ return headerOverride;
33
+ }
34
+
35
+ const mappedMethod = knownMethods[httpMethod];
36
+
37
+ if (mappedMethod) {
38
+ return mappedMethod;
39
+ }
40
+
41
+ if (httpMethod === 'get') {
42
+ return id === null ? 'find' : 'get';
43
+ }
44
+
45
+ throw new MethodNotAllowed(`Method ${_httpMethod} not allowed`);
46
+ }
47
+
48
+ export const argumentsFor = {
49
+ get: ({ id, params }: ServiceParams) => [ id, params ],
50
+ find: ({ params }: ServiceParams) => [ params ],
51
+ create: ({ data, params }: ServiceParams) => [ data, params ],
52
+ update: ({ id, data, params }: ServiceParams) => [ id, data, params ],
53
+ patch: ({ id, data, params }: ServiceParams) => [ id, data, params ],
54
+ remove: ({ id, params }: ServiceParams) => [ id, params ],
55
+ default: ({ data, params }: ServiceParams) => [ data, params ]
56
+ }
57
+
58
+ export function getStatusCode (context: HookContext, body: any, location: string|string[]) {
59
+ const { http = {} } = context;
60
+
61
+ if (http.status) {
62
+ return http.status;
63
+ }
64
+
65
+ if (context.method === 'create') {
66
+ return statusCodes.created;
67
+ }
68
+
69
+ if (location !== undefined) {
70
+ return statusCodes.seeOther;
71
+ }
72
+
73
+ if (!body) {
74
+ return statusCodes.noContent;
75
+ }
76
+
77
+ return statusCodes.success;
78
+ }
79
+
80
+ export function getResponse (context: HookContext) {
81
+ const { http = {} } = context;
82
+ const body = context.dispatch !== undefined ? context.dispatch : context.result;
83
+
84
+ let headers = http.headers || {};
85
+ let location = headers.Location;
86
+
87
+ if (http.location !== undefined) {
88
+ location = encodeUrl(http.location);
89
+ headers = { ...headers, Location: location };
90
+ }
91
+
92
+ const status = getStatusCode(context, body, location);
93
+
94
+ return { status, headers, body };
95
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  import { socket } from './socket';
2
2
  import { routing } from './routing';
3
- import { channels } from './channels';
3
+ import { channels, Channel, CombinedChannel, RealTimeConnection } from './channels';
4
4
 
5
- export { socket, routing, channels };
5
+ export * as http from './http';
6
+ export {
7
+ socket,
8
+ routing,
9
+ channels,
10
+ Channel,
11
+ CombinedChannel,
12
+ RealTimeConnection
13
+ };
@@ -1,45 +1,50 @@
1
- import { Application, Service } from '@feathersjs/feathers';
1
+ import { Application, Service, ServiceOptions } from '@feathersjs/feathers';
2
2
  import { Router } from './router';
3
3
 
4
4
  declare module '@feathersjs/feathers/lib/declarations' {
5
5
  interface RouteLookup {
6
- service: Service<any>,
7
- params: { [key: string]: string }
6
+ service: Service,
7
+ params: { [key: string]: any }
8
8
  }
9
9
 
10
- interface Application<ServiceTypes, AppSettings> { // eslint-disable-line
11
- routes: Router<any>;
10
+ interface Application<Services, Settings> { // eslint-disable-line
11
+ routes: Router<{
12
+ service: Service,
13
+ params?: { [key: string]: any }
14
+ }>;
12
15
  lookup (path: string): RouteLookup;
13
16
  }
14
17
  }
15
18
 
16
19
  export * from './router';
17
20
 
18
- export const routing = () => (app: Application) => {
19
- if (typeof app.lookup === 'function') {
20
- return;
21
+ const lookup = function (this: Application, path: string) {
22
+ const result = this.routes.lookup(path);
23
+
24
+ if (result === null) {
25
+ return null;
21
26
  }
22
27
 
23
- const routes = new Router();
28
+ const { params: colonParams, data: { service, params: dataParams } } = result;
24
29
 
25
- Object.assign(app, {
26
- routes,
27
- lookup (this: Application, path: string) {
28
- const result = this.routes.lookup(path);
30
+ const params = dataParams ? { ...dataParams, ...colonParams } : colonParams;
29
31
 
30
- if (result !== null) {
31
- const { params, data: service } = result;
32
+ return { service, params };
33
+ };
32
34
 
33
- return { params, service };
34
- }
35
+ export const routing = () => (app: Application) => {
36
+ if (typeof app.lookup === 'function') {
37
+ return;
38
+ }
35
39
 
36
- return result;
37
- }
38
- });
40
+ app.routes = new Router();
41
+ app.lookup = lookup;
39
42
 
40
43
  // Add a mixin that registers a service on the router
41
- app.mixins.push((service: Service<any>, path: string) => {
42
- app.routes.insert(path, service);
43
- app.routes.insert(`${path}/:__id`, service);
44
+ app.mixins.push((service: Service, path: string, options: ServiceOptions) => {
45
+ const { routeParams: params = {} } = options;
46
+
47
+ app.routes.insert(path, { service, params });
48
+ app.routes.insert(`${path}/:__id`, { service, params });
44
49
  });
45
50
  };
@@ -1,5 +1,4 @@
1
1
  import { stripSlashes } from '@feathersjs/commons';
2
- import { BadRequest } from '@feathersjs/errors';
3
2
 
4
3
  export interface LookupData {
5
4
  params: { [key: string]: string };
@@ -12,62 +11,74 @@ export interface LookupResult<T> extends LookupData {
12
11
  export class RouteNode<T = any> {
13
12
  data?: T;
14
13
  children: { [key: string]: RouteNode } = {};
15
- placeholder?: RouteNode;
14
+ placeholders: RouteNode[] = [];
16
15
 
17
- constructor (public name: string) {}
16
+ constructor (public name: string, public depth: number) {}
18
17
 
19
18
  insert (path: string[], data: T): RouteNode<T> {
20
- if (path.length === 0) {
19
+ if (this.depth === path.length) {
20
+ if (this.data !== undefined) {
21
+ throw new Error(`Path ${path.join('/')} already exists`);
22
+ }
23
+
21
24
  this.data = data;
22
25
  return this;
23
26
  }
24
27
 
25
- const [ current, ...rest ] = path;
28
+ const current = path[this.depth];
29
+ const nextDepth = this.depth + 1;
26
30
 
27
31
  if (current.startsWith(':')) {
28
- const { placeholder } = this;
29
- const name = current.substring(1);
32
+ // Insert a placeholder node like /messages/:id
33
+ const placeholderName = current.substring(1);
34
+ let placeholder = this.placeholders.find(p => p.name === placeholderName);
30
35
 
31
36
  if (!placeholder) {
32
- this.placeholder = new RouteNode(name);
33
- } else if(placeholder.name !== name) {
34
- throw new BadRequest(`Can not add route with placeholder ':${name}' because placeholder ':${placeholder.name}' already exists`);
37
+ placeholder = new RouteNode(placeholderName, nextDepth);
38
+ this.placeholders.push(placeholder);
35
39
  }
36
40
 
37
- return this.placeholder.insert(rest, data);
41
+ return placeholder.insert(path, data);
38
42
  }
39
43
 
40
- this.children[current] = this.children[current] || new RouteNode(current);
44
+ const child = this.children[current] || new RouteNode(current, nextDepth);
45
+
46
+ this.children[current] = child;
41
47
 
42
- return this.children[current].insert(rest, data);
48
+ return child.insert(path, data);
43
49
  }
44
50
 
45
51
  lookup (path: string[], info: LookupData): LookupResult<T>|null {
46
- if (path.length === 0) {
47
- return {
52
+ if (path.length === this.depth) {
53
+ return this.data === undefined ? null : {
48
54
  ...info,
49
55
  data: this.data
50
56
  }
51
57
  }
52
58
 
53
- const [ current, ...rest ] = path;
59
+ const current = path[this.depth];
54
60
  const child = this.children[current];
55
61
 
56
62
  if (child) {
57
- return child.lookup(rest, info);
63
+ return child.lookup(path, info);
58
64
  }
59
65
 
60
- if (this.placeholder) {
61
- info.params[this.placeholder.name] = current;
62
- return this.placeholder.lookup(rest, info);
66
+ // This will return the first placeholder that matches early
67
+ for(const placeholder of this.placeholders) {
68
+ const result = placeholder.lookup(path, info);
69
+
70
+ if (result !== null) {
71
+ result.params[placeholder.name] = current;
72
+ return result;
73
+ }
63
74
  }
64
75
 
65
76
  return null;
66
77
  }
67
78
  }
68
79
 
69
- export class Router<T> {
70
- root: RouteNode<T> = new RouteNode<T>('');
80
+ export class Router<T = any> {
81
+ constructor (public root: RouteNode<T> = new RouteNode<T>('', 0)) {}
71
82
 
72
83
  getPath (path: string) {
73
84
  return stripSlashes(path).split('/');
@@ -1,11 +1,11 @@
1
1
  import { Application, getServiceOptions, Params } from '@feathersjs/feathers';
2
- import Debug from 'debug';
2
+ import { createDebug } from '@feathersjs/commons';
3
3
  import { channels } from '../channels';
4
4
  import { routing } from '../routing';
5
5
  import { getDispatcher, runMethod } from './utils';
6
6
  import { RealTimeConnection } from '../channels/channel/base';
7
7
 
8
- const debug = Debug('@feathersjs/transport-commons');
8
+ const debug = createDebug('@feathersjs/transport-commons');
9
9
 
10
10
  export interface SocketOptions {
11
11
  done: Promise<any>;
@@ -1,11 +1,11 @@
1
- import Debug from 'debug';
2
- import isEqual from 'lodash/isEqual';
3
- import { NotFound, MethodNotAllowed, BadRequest } from '@feathersjs/errors';
4
1
  import { HookContext, Application, createContext, getServiceOptions } from '@feathersjs/feathers';
2
+ import { NotFound, MethodNotAllowed, BadRequest } from '@feathersjs/errors';
3
+ import { createDebug } from '@feathersjs/commons';
4
+ import isEqual from 'lodash/isEqual';
5
5
  import { CombinedChannel } from '../channels/channel/combined';
6
6
  import { RealTimeConnection } from '../channels/channel/base';
7
7
 
8
- const debug = Debug('@feathersjs/transport-commons');
8
+ const debug = createDebug('@feathersjs/transport-commons');
9
9
 
10
10
  export const DEFAULT_PARAMS_POSITION = 1;
11
11
 
@@ -91,7 +91,7 @@ export async function runMethod (app: Application, connection: RealTimeConnectio
91
91
  }
92
92
 
93
93
  const position = paramsPositions[method] !== undefined ? paramsPositions[method] : DEFAULT_PARAMS_POSITION;
94
- const query = methodArgs[position] || {};
94
+ const query = Object.assign({}, methodArgs[position]);
95
95
  // `params` have to be re-mapped to the query and added with the route
96
96
  const params = Object.assign({ query, route, connection }, connection);
97
97
 
@@ -108,7 +108,7 @@ export async function runMethod (app: Application, connection: RealTimeConnectio
108
108
 
109
109
  debug(`Returned successfully ${trace}`, result);
110
110
  callback(null, result);
111
- } catch (error) {
111
+ } catch (error: any) {
112
112
  handleError(error);
113
113
  }
114
114
  }