forked from strongloop/loopback-component-explorer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
153 lines (129 loc) · 5.12 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
// Node module: loopback-component-explorer
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
/*!
* Adds dynamically-updated docs as /explorer
*/
const url = require('url');
const path = require('path');
const urlJoin = require('./lib/url-join');
const _defaults = require('lodash').defaults;
const createSwaggerObject = require('loopback-swagger').generateSwaggerSpec;
const SWAGGER_UI_ROOT = require('swagger-ui-dist').getAbsoluteFSPath();
const STATIC_ROOT = path.join(__dirname, 'public');
function addConsumesOnFormData(swaggerObject) {
// change swaggerObject.paths.parameters that have "in": "formData" and add "consumes": ["application/x-www-form-urlencoded"]
for (let path in swaggerObject.paths) {
for (let method in swaggerObject.paths[path]) {
let parameters = swaggerObject.paths[path][method].parameters;
if (parameters) {
for (let i = 0; i < parameters.length; i++) {
if (parameters[i].in === 'formData') {
swaggerObject.paths[path][method].consumes = ['application/x-www-form-urlencoded'];
break;
}
}
}
}
}
}
/**
* Setup Swagger documentation on the given express app.
*
* @param {Application} loopbackApplication The loopback application to
* document.
* @param {Application} swaggerApp Swagger application used for hosting
* swagger documentation.
* @param {Object} opts Options.
*/
function mountSwagger(loopbackApplication, swaggerApp, opts) {
let swaggerObject = createSwaggerObject(loopbackApplication, opts);
addConsumesOnFormData(swaggerObject);
function rebuildSwaggerObject() {
swaggerObject = createSwaggerObject(loopbackApplication, opts);
addConsumesOnFormData(swaggerObject);
}
// listening to modelRemoted event for updating the swaggerObject
// with the newly created model to appear in the Swagger UI.
loopbackApplication.on('modelRemoted', rebuildSwaggerObject);
loopbackApplication.on('modelDeleted', rebuildSwaggerObject);
// listening to started event for updating the swaggerObject
// when a call to app.models.[modelName].nestRemoting([modelName])
// to appear that method in the Swagger UI.
loopbackApplication.on('remoteMethodAdded', rebuildSwaggerObject);
// listening to remoteMethodDisabled event for updating the swaggerObject
// when a remote method is disabled to hide that method in the Swagger UI.
loopbackApplication.on('remoteMethodDisabled', rebuildSwaggerObject);
let resourcePath = (opts && opts.resourcePath) || 'swagger.json';
if (resourcePath[0] !== '/') resourcePath = '/' + resourcePath;
swaggerApp.get(resourcePath, function sendSwaggerObject(req, res) {
res.status(200).send(swaggerObject);
});
}
function routes(loopbackApplication, options) {
const loopback = loopbackApplication.loopback;
options = _defaults({}, options, {
resourcePath: 'swagger.json',
apiInfo: loopbackApplication.get('apiInfo') || {},
swaggerUI: true,
});
const router = new loopback.Router();
mountSwagger(loopbackApplication, router, options);
// config.json is loaded by swagger-ui. The server should respond
// with the relative URI of the resource doc.
router.get('/config.json', function(req, res) {
// Get the path we're mounted at. It's best to get this from the referer
// in case we're proxied at a deep path.
let source = url.parse(req.headers.referer || '').pathname;
// strip index.html if present in referer
if (source && /\/index\.html$/.test(source)) {
source = source.replace(/\/index\.html$/, '');
}
// If no referer is available, use the incoming url.
if (!source) {
source = req.originalUrl.replace(/\/config.json(\?.*)?$/, '');
}
res.send({
url: urlJoin(source, '/' + options.resourcePath),
auth: options.auth,
});
});
if (options.swaggerUI) {
// Allow specifying a static file roots for swagger files. Any files in
// these folders will override those in the swagger-ui distribution.
// In this way one could e.g. make changes to index.html without having
// to worry about constantly pulling in JS updates.
if (options.uiDirs) {
if (typeof options.uiDirs === 'string') {
router.use(loopback.static(options.uiDirs));
} else if (Array.isArray(options.uiDirs)) {
options.uiDirs.forEach(function(dir) {
router.use(loopback.static(dir));
});
}
}
// File in node_modules are overridden by a few customizations
router.use(loopback.static(STATIC_ROOT));
// Swagger UI distribution
router.use(loopback.static(SWAGGER_UI_ROOT));
}
return router;
}
/**
* Example usage:
*
* var explorer = require('loopback-component-explorer');
* explorer(app, options);
*/
function explorer(loopbackApplication, options) {
options = _defaults({}, options, {mountPath: '/explorer'});
loopbackApplication.use(
options.mountPath,
routes(loopbackApplication, options)
);
loopbackApplication.set('loopback-component-explorer', options);
}
module.exports = explorer;
explorer.routes = routes;