@feathersjs/express 5.0.0-pre.3 → 5.0.0-pre.31
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/CHANGELOG.md +178 -214
- package/LICENSE +1 -1
- package/README.md +2 -2
- package/lib/authentication.d.ts +5 -6
- package/lib/authentication.js +27 -38
- package/lib/authentication.js.map +1 -1
- package/lib/declarations.d.ts +16 -11
- package/lib/handlers.js +5 -3
- package/lib/handlers.js.map +1 -1
- package/lib/index.d.ts +8 -7
- package/lib/index.js +71 -47
- package/lib/index.js.map +1 -1
- package/lib/rest.d.ts +8 -20
- package/lib/rest.js +64 -118
- package/lib/rest.js.map +1 -1
- package/package.json +26 -20
- package/src/authentication.ts +48 -53
- package/src/declarations.ts +43 -35
- package/src/handlers.ts +52 -50
- package/src/index.ts +107 -64
- package/src/rest.ts +88 -131
package/src/handlers.ts
CHANGED
|
@@ -1,130 +1,132 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
import { NotFound, GeneralError } from '@feathersjs/errors'
|
|
3
|
-
import { Request, Response, NextFunction, ErrorRequestHandler, RequestHandler } from 'express'
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { NotFound, GeneralError } from '@feathersjs/errors'
|
|
3
|
+
import { Request, Response, NextFunction, ErrorRequestHandler, RequestHandler } from 'express'
|
|
4
4
|
|
|
5
5
|
const defaults = {
|
|
6
6
|
public: path.resolve(__dirname, '..', 'public'),
|
|
7
7
|
logger: console
|
|
8
|
-
}
|
|
9
|
-
const defaultHtmlError = path.resolve(defaults.public, 'default.html')
|
|
8
|
+
}
|
|
9
|
+
const defaultHtmlError = path.resolve(defaults.public, 'default.html')
|
|
10
10
|
|
|
11
|
-
export function notFound
|
|
11
|
+
export function notFound({ verbose = false } = {}): RequestHandler {
|
|
12
12
|
return function (req: Request, _res: Response, next: NextFunction) {
|
|
13
|
-
const url = `${req.url}
|
|
14
|
-
const message = `Page not found${verbose ? ': ' + url : ''}
|
|
13
|
+
const url = `${req.url}`
|
|
14
|
+
const message = `Page not found${verbose ? ': ' + url : ''}`
|
|
15
15
|
|
|
16
|
-
next(new NotFound(message, { url }))
|
|
17
|
-
}
|
|
16
|
+
next(new NotFound(message, { url }))
|
|
17
|
+
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export type ErrorHandlerOptions = {
|
|
21
|
-
public?: string
|
|
22
|
-
logger?: boolean|{ error?: (msg: any) => void
|
|
23
|
-
html?: any
|
|
21
|
+
public?: string
|
|
22
|
+
logger?: boolean | { error?: (msg: any) => void; info?: (msg: any) => void }
|
|
23
|
+
html?: any
|
|
24
24
|
json?: any
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
26
|
|
|
27
|
-
export function errorHandler
|
|
28
|
-
const options = Object.assign({}, defaults, _options)
|
|
27
|
+
export function errorHandler(_options: ErrorHandlerOptions = {}): ErrorRequestHandler {
|
|
28
|
+
const options = Object.assign({}, defaults, _options)
|
|
29
29
|
|
|
30
30
|
if (typeof options.html === 'undefined') {
|
|
31
31
|
options.html = {
|
|
32
32
|
401: path.resolve(options.public, '401.html'),
|
|
33
33
|
404: path.resolve(options.public, '404.html'),
|
|
34
34
|
default: defaultHtmlError
|
|
35
|
-
}
|
|
35
|
+
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
if (typeof options.json === 'undefined') {
|
|
39
|
-
options.json = {}
|
|
39
|
+
options.json = {}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
return function (error: any, req: Request, res: Response, next: NextFunction) {
|
|
43
43
|
// Set the error code for HTTP processing semantics
|
|
44
|
-
error.code = !isNaN(parseInt(error.code, 10)) ? parseInt(error.code, 10) : 500
|
|
44
|
+
error.code = !isNaN(parseInt(error.code, 10)) ? parseInt(error.code, 10) : 500
|
|
45
45
|
|
|
46
46
|
// Log the error if it didn't come from a service method call
|
|
47
47
|
if (options.logger && typeof options.logger.error === 'function' && !res.hook) {
|
|
48
48
|
if (error.code >= 500) {
|
|
49
|
-
options.logger.error(error)
|
|
49
|
+
options.logger.error(error)
|
|
50
50
|
} else {
|
|
51
|
-
options.logger.info(error)
|
|
51
|
+
options.logger.info(error)
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
if (error.type !== 'FeathersError') {
|
|
56
|
-
const oldError = error
|
|
56
|
+
const oldError = error
|
|
57
57
|
|
|
58
|
-
error = oldError.errors
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
error = oldError.errors
|
|
59
|
+
? new GeneralError(oldError.message, {
|
|
60
|
+
errors: oldError.errors
|
|
61
|
+
})
|
|
62
|
+
: new GeneralError(oldError.message)
|
|
61
63
|
|
|
62
64
|
if (oldError.stack) {
|
|
63
|
-
error.stack = oldError.stack
|
|
65
|
+
error.stack = oldError.stack
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
const formatter: { [key: string]: any } = {}
|
|
69
|
+
const formatter: { [key: string]: any } = {}
|
|
68
70
|
|
|
69
71
|
// If the developer passed a custom function for ALL html errors
|
|
70
72
|
if (typeof options.html === 'function') {
|
|
71
|
-
formatter['text/html'] = options.html
|
|
73
|
+
formatter['text/html'] = options.html
|
|
72
74
|
} else {
|
|
73
|
-
let file = options.html[error.code]
|
|
75
|
+
let file = options.html[error.code]
|
|
74
76
|
if (!file) {
|
|
75
|
-
file = options.html.default || defaultHtmlError
|
|
77
|
+
file = options.html.default || defaultHtmlError
|
|
76
78
|
}
|
|
77
79
|
// If the developer passed a custom function for individual html errors
|
|
78
80
|
if (typeof file === 'function') {
|
|
79
|
-
formatter['text/html'] = file
|
|
81
|
+
formatter['text/html'] = file
|
|
80
82
|
} else {
|
|
81
83
|
formatter['text/html'] = function () {
|
|
82
|
-
res.set('Content-Type', 'text/html')
|
|
83
|
-
res.sendFile(file)
|
|
84
|
-
}
|
|
84
|
+
res.set('Content-Type', 'text/html')
|
|
85
|
+
res.sendFile(file)
|
|
86
|
+
}
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
// If the developer passed a custom function for ALL json errors
|
|
89
91
|
if (typeof options.json === 'function') {
|
|
90
|
-
formatter['application/json'] = options.json
|
|
92
|
+
formatter['application/json'] = options.json
|
|
91
93
|
} else {
|
|
92
|
-
const handler = options.json[error.code] || options.json.default
|
|
94
|
+
const handler = options.json[error.code] || options.json.default
|
|
93
95
|
// If the developer passed a custom function for individual json errors
|
|
94
96
|
if (typeof handler === 'function') {
|
|
95
|
-
formatter['application/json'] = handler
|
|
97
|
+
formatter['application/json'] = handler
|
|
96
98
|
} else {
|
|
97
99
|
// Don't show stack trace if it is a 404 error
|
|
98
100
|
if (error.code === 404) {
|
|
99
|
-
error.stack = null
|
|
101
|
+
error.stack = null
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
formatter['application/json'] = function () {
|
|
103
|
-
const output = Object.assign({}, error.toJSON())
|
|
105
|
+
const output = Object.assign({}, error.toJSON())
|
|
104
106
|
|
|
105
107
|
if (process.env.NODE_ENV === 'production') {
|
|
106
|
-
delete output.stack
|
|
108
|
+
delete output.stack
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
res.set('Content-Type', 'application/json')
|
|
110
|
-
res.json(output)
|
|
111
|
-
}
|
|
111
|
+
res.set('Content-Type', 'application/json')
|
|
112
|
+
res.json(output)
|
|
113
|
+
}
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
res.status(error.code)
|
|
117
|
+
res.status(error.code)
|
|
116
118
|
|
|
117
|
-
const contentType = req.headers['content-type'] || ''
|
|
118
|
-
const accepts = req.headers.accept || ''
|
|
119
|
+
const contentType = req.headers['content-type'] || ''
|
|
120
|
+
const accepts = req.headers.accept || ''
|
|
119
121
|
|
|
120
122
|
// by default just send back json
|
|
121
123
|
if (contentType.indexOf('json') !== -1 || accepts.indexOf('json') !== -1) {
|
|
122
|
-
formatter['application/json'](error, req, res, next)
|
|
124
|
+
formatter['application/json'](error, req, res, next)
|
|
123
125
|
} else if (options.html && (contentType.indexOf('html') !== -1 || accepts.indexOf('html') !== -1)) {
|
|
124
|
-
formatter['text/html'](error, req, res, next)
|
|
126
|
+
formatter['text/html'](error, req, res, next)
|
|
125
127
|
} else {
|
|
126
128
|
// TODO (EK): Maybe just return plain text
|
|
127
|
-
formatter['application/json'](error, req, res, next)
|
|
129
|
+
formatter['application/json'](error, req, res, next)
|
|
128
130
|
}
|
|
129
|
-
}
|
|
131
|
+
}
|
|
130
132
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,107 +1,150 @@
|
|
|
1
|
-
import express, {
|
|
2
|
-
|
|
3
|
-
} from '
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
import { Application } from './declarations'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
1
|
+
import express, { Express } from 'express'
|
|
2
|
+
import { Application as FeathersApplication, defaultServiceMethods } from '@feathersjs/feathers'
|
|
3
|
+
import { routing } from '@feathersjs/transport-commons'
|
|
4
|
+
import { createDebug } from '@feathersjs/commons'
|
|
5
|
+
import cors from 'cors'
|
|
6
|
+
|
|
7
|
+
import { rest, RestOptions, formatter } from './rest'
|
|
8
|
+
import { errorHandler, notFound, ErrorHandlerOptions } from './handlers'
|
|
9
|
+
import { Application, ExpressOverrides } from './declarations'
|
|
10
|
+
import { AuthenticationSettings, authenticate, parseAuthentication } from './authentication'
|
|
11
|
+
import { default as original, static as serveStatic, json, raw, text, urlencoded, query } from 'express'
|
|
12
12
|
|
|
13
13
|
export {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
original,
|
|
15
|
+
serveStatic,
|
|
16
|
+
serveStatic as static,
|
|
17
|
+
json,
|
|
18
|
+
raw,
|
|
19
|
+
text,
|
|
20
|
+
urlencoded,
|
|
21
|
+
query,
|
|
22
|
+
rest,
|
|
23
|
+
RestOptions,
|
|
24
|
+
formatter,
|
|
25
|
+
errorHandler,
|
|
26
|
+
notFound,
|
|
27
|
+
Application,
|
|
28
|
+
ErrorHandlerOptions,
|
|
29
|
+
ExpressOverrides,
|
|
30
|
+
AuthenticationSettings,
|
|
31
|
+
parseAuthentication,
|
|
32
|
+
authenticate,
|
|
33
|
+
cors
|
|
34
|
+
}
|
|
21
35
|
|
|
22
|
-
const debug = createDebug('@feathersjs/express')
|
|
36
|
+
const debug = createDebug('@feathersjs/express')
|
|
23
37
|
|
|
24
|
-
export default function feathersExpress<S = any, C = any>
|
|
38
|
+
export default function feathersExpress<S = any, C = any>(
|
|
39
|
+
feathersApp?: FeathersApplication<S, C>,
|
|
40
|
+
expressApp: Express = express()
|
|
41
|
+
): Application<S, C> {
|
|
25
42
|
if (!feathersApp) {
|
|
26
|
-
return expressApp as any
|
|
43
|
+
return expressApp as any
|
|
27
44
|
}
|
|
28
45
|
|
|
29
46
|
if (typeof feathersApp.setup !== 'function') {
|
|
30
|
-
throw new Error('@feathersjs/express requires a valid Feathers application instance')
|
|
47
|
+
throw new Error('@feathersjs/express requires a valid Feathers application instance')
|
|
31
48
|
}
|
|
32
49
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
50
|
+
const app = expressApp as any as Application<S, C>
|
|
51
|
+
const { use: expressUse, listen: expressListen } = expressApp as any
|
|
52
|
+
const { use: feathersUse, teardown: feathersTeardown } = feathersApp
|
|
53
|
+
|
|
54
|
+
Object.assign(app, {
|
|
55
|
+
use(location: string & keyof S, ...rest: any[]) {
|
|
56
|
+
let service: any
|
|
57
|
+
let options = {}
|
|
39
58
|
|
|
40
|
-
const middleware = rest.reduce(
|
|
59
|
+
const middleware = rest.reduce(
|
|
60
|
+
function (middleware, arg) {
|
|
41
61
|
if (typeof arg === 'function' || Array.isArray(arg)) {
|
|
42
|
-
middleware[service ? 'after' : 'before'].push(arg)
|
|
62
|
+
middleware[service ? 'after' : 'before'].push(arg)
|
|
43
63
|
} else if (!service) {
|
|
44
|
-
service = arg
|
|
45
|
-
} else if (arg.methods || arg.events) {
|
|
46
|
-
options = arg
|
|
64
|
+
service = arg
|
|
65
|
+
} else if (arg.methods || arg.events || arg.express || arg.koa) {
|
|
66
|
+
options = arg
|
|
47
67
|
} else {
|
|
48
|
-
throw new Error('Invalid options passed to app.use')
|
|
68
|
+
throw new Error('Invalid options passed to app.use')
|
|
49
69
|
}
|
|
50
|
-
return middleware
|
|
51
|
-
},
|
|
70
|
+
return middleware
|
|
71
|
+
},
|
|
72
|
+
{
|
|
52
73
|
before: [],
|
|
53
74
|
after: []
|
|
54
|
-
}
|
|
75
|
+
}
|
|
76
|
+
)
|
|
55
77
|
|
|
56
|
-
const hasMethod = (methods: string[]) =>
|
|
57
|
-
(service && typeof service[name] === 'function')
|
|
58
|
-
);
|
|
78
|
+
const hasMethod = (methods: string[]) =>
|
|
79
|
+
methods.some((name) => service && typeof service[name] === 'function')
|
|
59
80
|
|
|
60
81
|
// Check for service (any object with at least one service method)
|
|
61
82
|
if (hasMethod(['handle', 'set']) || !hasMethod(defaultServiceMethods)) {
|
|
62
|
-
debug('Passing app.use call to Express app')
|
|
63
|
-
return
|
|
83
|
+
debug('Passing app.use call to Express app')
|
|
84
|
+
return expressUse.call(this, location, ...rest)
|
|
64
85
|
}
|
|
65
86
|
|
|
66
|
-
debug('Registering service with middleware', middleware)
|
|
87
|
+
debug('Registering service with middleware', middleware)
|
|
67
88
|
// Since this is a service, call Feathers `.use`
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
})
|
|
89
|
+
feathersUse.call(this, location, service, {
|
|
90
|
+
express: middleware,
|
|
91
|
+
...options
|
|
92
|
+
})
|
|
72
93
|
|
|
73
|
-
return this
|
|
94
|
+
return this
|
|
74
95
|
},
|
|
75
96
|
|
|
76
|
-
async listen
|
|
77
|
-
const server =
|
|
97
|
+
async listen(...args: any[]) {
|
|
98
|
+
const server = expressListen.call(this, ...args)
|
|
78
99
|
|
|
79
|
-
|
|
80
|
-
|
|
100
|
+
this.server = server
|
|
101
|
+
await this.setup(server)
|
|
102
|
+
debug('Feathers application listening')
|
|
81
103
|
|
|
82
|
-
return server
|
|
104
|
+
return server
|
|
83
105
|
}
|
|
84
|
-
}
|
|
106
|
+
} as Application<S, C>)
|
|
85
107
|
|
|
86
|
-
const
|
|
108
|
+
const appDescriptors = {
|
|
109
|
+
...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(app)),
|
|
110
|
+
...Object.getOwnPropertyDescriptors(app)
|
|
111
|
+
}
|
|
112
|
+
const newDescriptors = {
|
|
87
113
|
...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(feathersApp)),
|
|
88
114
|
...Object.getOwnPropertyDescriptors(feathersApp)
|
|
89
|
-
}
|
|
115
|
+
}
|
|
90
116
|
|
|
91
117
|
// Copy all non-existing properties (including non-enumerables)
|
|
92
118
|
// that don't already exist on the Express app
|
|
93
|
-
Object.keys(
|
|
94
|
-
const
|
|
95
|
-
const
|
|
119
|
+
Object.keys(newDescriptors).forEach((prop) => {
|
|
120
|
+
const appProp = appDescriptors[prop]
|
|
121
|
+
const newProp = newDescriptors[prop]
|
|
96
122
|
|
|
97
|
-
if (
|
|
98
|
-
Object.defineProperty(expressApp, prop,
|
|
123
|
+
if (appProp === undefined && newProp !== undefined) {
|
|
124
|
+
Object.defineProperty(expressApp, prop, newProp)
|
|
99
125
|
}
|
|
100
|
-
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Assign teardown and setup which will also make sure that hooks are initialized
|
|
129
|
+
app.setup = feathersApp.setup as any
|
|
130
|
+
app.teardown = async function teardown(server?: any) {
|
|
131
|
+
return feathersTeardown.call(this, server).then(
|
|
132
|
+
() =>
|
|
133
|
+
new Promise((resolve, reject) => {
|
|
134
|
+
if (this.server) {
|
|
135
|
+
this.server.close((e) => (e ? reject(e) : resolve(this)))
|
|
136
|
+
} else {
|
|
137
|
+
resolve(this)
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
app.configure(routing() as any)
|
|
101
144
|
|
|
102
|
-
return
|
|
145
|
+
return app
|
|
103
146
|
}
|
|
104
147
|
|
|
105
148
|
if (typeof module !== 'undefined') {
|
|
106
|
-
module.exports = Object.assign(feathersExpress, module.exports)
|
|
149
|
+
module.exports = Object.assign(feathersExpress, module.exports)
|
|
107
150
|
}
|
package/src/rest.ts
CHANGED
|
@@ -1,160 +1,117 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { createDebug } from '@feathersjs/commons'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { Request, Response, RequestHandler, Router } from 'express'
|
|
2
|
+
import { MethodNotAllowed } from '@feathersjs/errors'
|
|
3
|
+
import { createDebug } from '@feathersjs/commons'
|
|
4
|
+
import { http } from '@feathersjs/transport-commons'
|
|
5
|
+
import { createContext, defaultServiceMethods, getServiceOptions } from '@feathersjs/feathers'
|
|
6
6
|
|
|
7
|
-
import { parseAuthentication } from './authentication'
|
|
7
|
+
import { AuthenticationSettings, parseAuthentication } from './authentication'
|
|
8
|
+
import { Application } from './declarations'
|
|
8
9
|
|
|
9
|
-
const debug = createDebug('@feathersjs/express/rest')
|
|
10
|
+
const debug = createDebug('@feathersjs/express/rest')
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
data: any,
|
|
16
|
-
params: Params
|
|
12
|
+
const toHandler = (
|
|
13
|
+
func: (req: Request, res: Response, next: () => void) => Promise<void>
|
|
14
|
+
): RequestHandler => {
|
|
15
|
+
return (req, res, next) => func(req, res, next).catch((error) => next(error))
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
noContent: 204,
|
|
24
|
-
methodNotAllowed: 405,
|
|
25
|
-
success: 200
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const feathersParams = (req: Request, _res: Response, next: NextFunction) => {
|
|
29
|
-
req.feathers = {
|
|
30
|
-
...req.feathers,
|
|
31
|
-
provider: 'rest',
|
|
32
|
-
headers: req.headers
|
|
33
|
-
};
|
|
34
|
-
next();
|
|
35
|
-
}
|
|
18
|
+
const serviceMiddleware = (): RequestHandler => {
|
|
19
|
+
return toHandler(async (req, res, next) => {
|
|
20
|
+
const { query, headers, path, body: data, method: httpMethod } = req
|
|
21
|
+
const methodOverride = req.headers[http.METHOD_HEADER] as string | undefined
|
|
36
22
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
24
|
+
const { service, params: { __id: id = null, ...route } = {} } = req.lookup!
|
|
25
|
+
const method = http.getServiceMethod(httpMethod, id, methodOverride)
|
|
26
|
+
const { methods } = getServiceOptions(service)
|
|
41
27
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
28
|
+
debug(`Found service for path ${path}, attempting to run '${method}' service method`)
|
|
29
|
+
|
|
30
|
+
if (!methods.includes(method) || defaultServiceMethods.includes(methodOverride)) {
|
|
31
|
+
const error = new MethodNotAllowed(`Method \`${method}\` is not supported by this endpoint.`)
|
|
32
|
+
res.statusCode = error.code
|
|
33
|
+
throw error
|
|
45
34
|
}
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
35
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
36
|
+
const createArguments = http.argumentsFor[method as 'get'] || http.argumentsFor.default
|
|
37
|
+
const params = { query, headers, route, ...req.feathers }
|
|
38
|
+
const args = createArguments({ id, data, params })
|
|
39
|
+
const contextBase = createContext(service, method, { http: {} })
|
|
40
|
+
res.hook = contextBase
|
|
41
|
+
|
|
42
|
+
const context = await (service as any)[method](...args, contextBase)
|
|
43
|
+
res.hook = context
|
|
53
44
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
45
|
+
const response = http.getResponse(context)
|
|
46
|
+
res.statusCode = response.status
|
|
47
|
+
res.set(response.headers)
|
|
48
|
+
res.data = response.body
|
|
49
|
+
|
|
50
|
+
return next()
|
|
51
|
+
})
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
54
|
+
const servicesMiddleware = (): RequestHandler => {
|
|
55
|
+
return toHandler(async (req, res, next) => {
|
|
56
|
+
const app = req.app as any as Application
|
|
57
|
+
const lookup = app.lookup(req.path)
|
|
64
58
|
|
|
65
|
-
if (
|
|
66
|
-
return
|
|
59
|
+
if (!lookup) {
|
|
60
|
+
return next()
|
|
67
61
|
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (!res.data) {
|
|
71
|
-
return statusCodes.noContent;
|
|
72
|
-
}
|
|
73
62
|
|
|
74
|
-
|
|
75
|
-
}
|
|
63
|
+
req.lookup = lookup
|
|
76
64
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
debug(`Running service middleware for '${req.url}'`);
|
|
65
|
+
const options = getServiceOptions(lookup.service)
|
|
66
|
+
const middleware = options.express.composed
|
|
80
67
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const params = { query, route, ...req.feathers };
|
|
85
|
-
const context = await callback(req, res, { id, data, params });
|
|
68
|
+
return middleware(req, res, next)
|
|
69
|
+
})
|
|
70
|
+
}
|
|
86
71
|
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
export const formatter: RequestHandler = (_req, res, next) => {
|
|
73
|
+
if (res.data === undefined) {
|
|
74
|
+
return next()
|
|
75
|
+
}
|
|
89
76
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
77
|
+
res.format({
|
|
78
|
+
'application/json'() {
|
|
79
|
+
res.json(res.data)
|
|
93
80
|
}
|
|
94
|
-
}
|
|
81
|
+
})
|
|
82
|
+
}
|
|
95
83
|
|
|
96
|
-
export
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const method = methodOverride ? methodOverride : methodName
|
|
101
|
-
const { methods } = getServiceOptions(service);
|
|
84
|
+
export type RestOptions = {
|
|
85
|
+
formatter?: RequestHandler
|
|
86
|
+
authentication?: AuthenticationSettings
|
|
87
|
+
}
|
|
102
88
|
|
|
103
|
-
|
|
104
|
-
|
|
89
|
+
export const rest = (options?: RestOptions | RequestHandler) => {
|
|
90
|
+
options = typeof options === 'function' ? { formatter: options } : options || {}
|
|
105
91
|
|
|
106
|
-
|
|
107
|
-
|
|
92
|
+
const formatterMiddleware = options.formatter || formatter
|
|
93
|
+
const authenticationOptions = options.authentication
|
|
108
94
|
|
|
109
|
-
|
|
110
|
-
|
|
95
|
+
return (app: Application) => {
|
|
96
|
+
if (typeof app.route !== 'function') {
|
|
97
|
+
throw new Error('@feathersjs/express/rest needs an Express compatible app.')
|
|
98
|
+
}
|
|
111
99
|
|
|
112
|
-
|
|
100
|
+
app.use((req, _res, next) => {
|
|
101
|
+
req.feathers = { ...req.feathers, provider: 'rest' }
|
|
102
|
+
return next()
|
|
103
|
+
})
|
|
104
|
+
app.use(parseAuthentication(authenticationOptions))
|
|
105
|
+
app.use(servicesMiddleware())
|
|
113
106
|
|
|
114
|
-
|
|
115
|
-
}
|
|
107
|
+
app.mixins.push((_service, _path, options) => {
|
|
108
|
+
const { express: { before = [], after = [] } = {} } = options
|
|
116
109
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (typeof app.route !== 'function') {
|
|
120
|
-
throw new Error('@feathersjs/express/rest needs an Express compatible app.');
|
|
121
|
-
}
|
|
110
|
+
const middlewares = [].concat(before, serviceMiddleware(), after, formatterMiddleware)
|
|
111
|
+
const middleware = Router().use(middlewares)
|
|
122
112
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
app.mixins.push(function (service: any, path: string, options: any) {
|
|
128
|
-
const { middleware: { before = [] } } = options;
|
|
129
|
-
let { middleware: { after = [] } } = options;
|
|
130
|
-
|
|
131
|
-
if (typeof handler === 'function') {
|
|
132
|
-
after = after.concat(handler);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const baseUri = `/${path}`;
|
|
136
|
-
const find = serviceMethodHandler(service, 'find', ({ params }) => [ params ]);
|
|
137
|
-
const get = serviceMethodHandler(service, 'get', ({ id, params }) => [ id, params ]);
|
|
138
|
-
const create = serviceMethodHandler(service, 'create', ({ data, params }) => [ data, params ], METHOD_HEADER);
|
|
139
|
-
const update = serviceMethodHandler(service, 'update', ({ id, data, params }) => [ id, data, params ]);
|
|
140
|
-
const patch = serviceMethodHandler(service, 'patch', ({ id, data, params }) => [ id, data, params ]);
|
|
141
|
-
const remove = serviceMethodHandler(service, 'remove', ({ id, params }) => [ id, params ]);
|
|
142
|
-
|
|
143
|
-
debug(`Adding REST provider for service \`${path}\` at base route \`${baseUri}\``);
|
|
144
|
-
|
|
145
|
-
const idRoute = '/:__feathersId';
|
|
146
|
-
const serviceRouter = Router({ mergeParams: true })
|
|
147
|
-
.get('/', find)
|
|
148
|
-
.post('/', create)
|
|
149
|
-
.get(idRoute, get)
|
|
150
|
-
.put('/', update)
|
|
151
|
-
.put(idRoute, update)
|
|
152
|
-
.patch('/', patch)
|
|
153
|
-
.patch(idRoute, patch)
|
|
154
|
-
.delete('/', remove)
|
|
155
|
-
.delete(idRoute, remove);
|
|
156
|
-
|
|
157
|
-
app.use(baseUri, ...before, serviceRouter, ...after);
|
|
158
|
-
});
|
|
159
|
-
};
|
|
113
|
+
options.express ||= {}
|
|
114
|
+
options.express.composed = middleware
|
|
115
|
+
})
|
|
116
|
+
}
|
|
160
117
|
}
|