Compare commits
36 Commits
Author | SHA1 | Date |
---|---|---|
|
7bcdfa7350 | |
|
53b4808d33 | |
|
6ba2ba5dc6 | |
|
a18a6f57a1 | |
|
a2a357b968 | |
|
4149f0e7a3 | |
|
f4faf35575 | |
|
3098ea147f | |
|
bc9c69a9fe | |
|
8427632f35 | |
|
797bbb9518 | |
|
4c357492be | |
|
5a90fbe6c5 | |
|
06fae69f57 | |
|
2836377c49 | |
|
0edafc393c | |
|
0da66a7e09 | |
|
93b2c69128 | |
|
c3e503c52c | |
|
ae5c6be195 | |
|
c4f5b1458d | |
|
4abaa30cbf | |
|
45b51a7205 | |
|
b83d0c23a6 | |
|
df43c009a1 | |
|
8274f2b1fa | |
|
72316ac551 | |
|
79fbea19e2 | |
|
02cfb554ee | |
|
34f8b79f04 | |
|
87dec34709 | |
|
94b12fb52c | |
|
ca4a996d87 | |
|
80e86adff3 | |
|
246ba0b793 | |
|
ecac6b1a04 |
|
@ -0,0 +1,2 @@
|
|||
public
|
||||
coverage
|
|
@ -1,37 +0,0 @@
|
|||
<!--
|
||||
Questions:
|
||||
https://groups.google.com/forum/#!forum/loopbackjs
|
||||
https://gitter.im/strongloop/loopback
|
||||
Immediate support:
|
||||
https://strongloop.com/api-connect-faqs/
|
||||
https://strongloop.com/node-js/subscription-plans/
|
||||
-->
|
||||
|
||||
# Description/Steps to reproduce
|
||||
|
||||
<!--
|
||||
If feature: A description of the feature
|
||||
If bug: Steps to reproduce
|
||||
-->
|
||||
|
||||
# Link to reproduction sandbox
|
||||
|
||||
<!--
|
||||
Link to an app sandbox for reproduction
|
||||
|
||||
Note: Failure to provide a sandbox application for reproduction purposes will result in the issue being closed.
|
||||
-->
|
||||
|
||||
# Expected result
|
||||
|
||||
<!--
|
||||
Also include actual results if bug
|
||||
-->
|
||||
|
||||
# Additional information
|
||||
|
||||
<!--
|
||||
Copy+paste the output of these two commands:
|
||||
node -e 'console.log(process.platform, process.arch, process.versions.node)'
|
||||
npm ls --prod --depth 0 | grep loopback
|
||||
-->
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
labels: bug
|
||||
|
||||
---
|
||||
|
||||
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
|
||||
|
||||
HELP US HELP YOU, PLEASE
|
||||
- Do a quick search to avoid duplicate issues
|
||||
- Provide as much information as possible (reproduction sandbox, use case for features, etc.)
|
||||
- Consider using a more suitable venue for questions such as Stack Overflow, Gitter, etc.
|
||||
|
||||
Please fill in the *entire* template below.
|
||||
|
||||
-->
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
<!-- Describe how to reproduce the issue -->
|
||||
|
||||
## Current Behavior
|
||||
|
||||
<!-- Describe the observed result -->
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
<!-- Describe what did you expect instead, what is the desired outcome? -->
|
||||
|
||||
## Link to reproduction sandbox
|
||||
|
||||
<!--
|
||||
See https://loopback.io/doc/en/contrib/Reporting-issues.html#loopback-3x-bugs
|
||||
Note: Failure to provide a sandbox application for reproduction purposes will result in the issue being closed.
|
||||
-->
|
||||
|
||||
## Additional information
|
||||
|
||||
<!--
|
||||
Copy+paste the output of these two commands:
|
||||
node -e 'console.log(process.platform, process.arch, process.versions.node)'
|
||||
npm ls --prod --depth 0 | grep loopback
|
||||
-->
|
||||
|
||||
## Related Issues
|
||||
|
||||
<!-- Did you find other bugs that looked similar? -->
|
||||
|
||||
_See [Reporting Issues](http://loopback.io/doc/en/contrib/Reporting-issues.html) for more tips on writing good issues_
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
labels: feature
|
||||
|
||||
---
|
||||
|
||||
## Suggestion
|
||||
|
||||
<!-- A summary of what you'd like to see added or changed -->
|
||||
|
||||
## Use Cases
|
||||
|
||||
<!--
|
||||
What do you want to use this for?
|
||||
What shortcomings exist with current approaches?
|
||||
-->
|
||||
|
||||
## Examples
|
||||
|
||||
<!-- Show how this would be used and what the behavior would be -->
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
TBD - will be filled by the team.
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
name: Question
|
||||
about: The issue tracker is not for questions. Please use Stack Overflow or other resources for help.
|
||||
labels: question
|
||||
|
||||
---
|
||||
|
||||
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
|
||||
|
||||
THE ISSUE TRACKER IS NOT FOR QUESTIONS.
|
||||
|
||||
DO NOT CREATE A NEW ISSUE TO ASK A QUESTION.
|
||||
|
||||
Please use one of the following resources for help:
|
||||
|
||||
**Questions**
|
||||
|
||||
- https://stackoverflow.com/tags/loopbackjs
|
||||
- https://groups.google.com/forum/#!forum/loopbackjs
|
||||
- https://gitter.im/strongloop/loopback
|
||||
|
||||
**Immediate support**
|
||||
|
||||
- https://strongloop.com/api-connect-faqs/
|
||||
- https://strongloop.com/node-js/subscription-plans/
|
||||
|
||||
-->
|
|
@ -0,0 +1,11 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Report a security vulnerability
|
||||
url: https://loopback.io/doc/en/contrib/Reporting-issues.html#security-issues
|
||||
about: Do not report security vulnerabilities using GitHub issues. Please send an email to `reachsl@us.ibm.com` instead.
|
||||
- name: Get help on StackOverflow
|
||||
url: https://stackoverflow.com/tags/loopbackjs
|
||||
about: Please ask and answer questions on StackOverflow.
|
||||
- name: Join our mailing list
|
||||
url: https://groups.google.com/forum/#!forum/loopbackjs
|
||||
about: You can also post your question to our mailing list.
|
|
@ -1,25 +1,18 @@
|
|||
### Description
|
||||
|
||||
|
||||
#### Related issues
|
||||
|
||||
<!--
|
||||
Please use the following link syntaxes:
|
||||
Please provide a high-level description of the changes made by your pull request.
|
||||
|
||||
- connect to #49 (to reference issues in the current repository)
|
||||
- connect to strongloop/loopback#49 (to reference issues in another repository)
|
||||
Include references to all related GitHub issues and other pull requests, for example:
|
||||
|
||||
Fixes #123
|
||||
Implements #254
|
||||
See also #23
|
||||
-->
|
||||
|
||||
- connect to <link_to_referenced_issue>
|
||||
## Checklist
|
||||
|
||||
### Checklist
|
||||
|
||||
<!--
|
||||
- Please mark your choice with an "x" (i.e. [x], see
|
||||
https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments)
|
||||
- PR's without test coverage will be closed.
|
||||
-->
|
||||
👉 [Read and sign the CLA (Contributor License Agreement)](https://cla.strongloop.com/agreements/strongloop/loopback-component-explorer) 👈
|
||||
|
||||
- [ ] `npm test` passes on your machine
|
||||
- [ ] New tests added or existing tests modified to cover all changes
|
||||
- [ ] Code conforms with the [style
|
||||
guide](http://loopback.io/doc/en/contrib/style-guide.html)
|
||||
- [ ] Code conforms with the [style guide](https://loopback.io/doc/en/contrib/style-guide-es6.html)
|
||||
- [ ] Commit messages are following our [guidelines](https://loopback.io/doc/en/contrib/git-commit-messages.html)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- "6"
|
||||
- "8"
|
||||
- "10"
|
||||
- "12"
|
||||
|
|
50
CHANGES.md
50
CHANGES.md
|
@ -1,3 +1,53 @@
|
|||
2020-03-06, Version 6.5.1
|
||||
=========================
|
||||
|
||||
* Update LTS status in README (Miroslav Bajtoš)
|
||||
|
||||
* Cursor is made pointer of the add token button (Siraj Alam)
|
||||
|
||||
|
||||
2019-11-28, Version 6.5.0
|
||||
=========================
|
||||
|
||||
* docs: describe GitHub advisory CVE-2019-17495 (Miroslav Bajtoš)
|
||||
|
||||
* chore: improve README formatting (Miroslav Bajtoš)
|
||||
|
||||
* Update README on swagger-ui (Diana Lau)
|
||||
|
||||
* chore: improve issue and PR templates (Nora)
|
||||
|
||||
* chore: add Node.js 12 to travis ci (Nora)
|
||||
|
||||
* chore: drop support for Node.js 6 (Nora)
|
||||
|
||||
* update LTS (Diana Lau)
|
||||
|
||||
|
||||
2019-05-09, Version 6.4.0
|
||||
=========================
|
||||
|
||||
* chore: update copyrights years (Agnes Lin)
|
||||
|
||||
* update README for LTS status (Diana Lau)
|
||||
|
||||
* Updated README with small code fix. (Pradeep Kumar Tippa)
|
||||
|
||||
* fix: update lodash (jannyHou)
|
||||
|
||||
* Prevent accidental upgrades to swagger-ui v3+ (Miroslav Bajtoš)
|
||||
|
||||
* Update eslint & config to latest major versions (Miroslav Bajtoš)
|
||||
|
||||
* Update eslint & config to latest (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2018-10-18, Version 6.3.1
|
||||
=========================
|
||||
|
||||
* README: update LTS status (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2018-09-14, Version 6.3.0
|
||||
=========================
|
||||
|
||||
|
|
106
README.md
106
README.md
|
@ -1,15 +1,19 @@
|
|||
# loopback-component-explorer
|
||||
|
||||
**⚠️ LoopBack 3 is in Maintenance LTS mode, only critical bugs and critical
|
||||
security fixes will be provided. (See
|
||||
[Module Long Term Support Policy](#module-long-term-support-policy) below.)**
|
||||
|
||||
We urge all LoopBack 3 users to migrate their applications to LoopBack 4 as
|
||||
soon as possible. Refer to our
|
||||
[Migration Guide](https://loopback.io/doc/en/lb4/migration-overview.html)
|
||||
for more information on how to upgrade.
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
Browse and test your LoopBack app's APIs.
|
||||
|
||||
## Supported versions
|
||||
|
||||
Current|Long Term Support|Maintenance
|
||||
:-:|:-:|:-:
|
||||
6.x|5.x|4.x
|
||||
|
||||
Learn more about our LTS plan in [docs](http://loopback.io/doc/en/contrib/Long-term-support.html).
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Below is a simple LoopBack application. The explorer is mounted at `/explorer`.
|
||||
|
@ -36,6 +40,77 @@ console.log("Explorer mounted at localhost:" + port + "/explorer");
|
|||
app.listen(port);
|
||||
```
|
||||
|
||||
## A note on swagger-ui vulnerabilities
|
||||
|
||||
API Explorer for LoopBack 3 is built on top of `swagger-ui` version 2.x which
|
||||
is no longer maintained. While there are known security vulnerabilities in
|
||||
`swagger-ui`, we believe they don't affect LoopBack users.
|
||||
|
||||
We would love to upgrade our (LB3) API Explorer to v3 of swagger-ui, but
|
||||
unfortunately such upgrade requires too much effort and more importantly
|
||||
addition of new features to LB3 runtime, which would break our LTS guarantees.
|
||||
For more details, see discussion in
|
||||
[loopback-component-explorer#263](https://github.com/strongloop/loopback-component-explorer/issues/263).
|
||||
|
||||
### npm advisory 985
|
||||
|
||||
Link: https://www.npmjs.com/advisories/985
|
||||
|
||||
> Versions of swagger-ui prior to 3.0.13 are vulnerable to Cross-Site Scripting
|
||||
> (XSS). The package fails to sanitize YAML files imported from URLs or
|
||||
> copied-pasted. This may allow attackers to execute arbitrary JavaScript.
|
||||
|
||||
LoopBack's API Explorer does not allow clients to import swagger spec from YAML
|
||||
URL/pasted-content. That means loopback-component-explorer **IS NOT AFFECTED**
|
||||
by this vulnerability.
|
||||
|
||||
### npm advisory 975
|
||||
|
||||
Link: https://www.npmjs.com/advisories/975
|
||||
|
||||
> Versions of swagger-ui prior to 3.18.0 are vulnerable to Reverse Tabnapping.
|
||||
> The package uses `target='_blank'` in anchor tags, allowing attackers to
|
||||
> access `window.opener` for the original page. This is commonly used for
|
||||
> phishing attacks.
|
||||
|
||||
This vulnerability affects anchor tags created from metadata provided by the
|
||||
Swagger spec, for example `info.termsOfServiceUrl`. LoopBack's API Explorer
|
||||
does not allow clients to provide custom swagger spec, URLs like
|
||||
`info.termsOfServiceUrl` are fully in control of the LoopBack application
|
||||
developer. That means loopback-component-explorer **IS NOT AFFECTED** by this
|
||||
vulnerability.
|
||||
|
||||
### npm advisory 976
|
||||
|
||||
Link: https://www.npmjs.com/advisories/976
|
||||
|
||||
> Versions of swagger-ui prior to 3.20.9 are vulnerable to Cross-Site Scripting
|
||||
> (XSS). The package fails to sanitize URLs used in the OAuth auth flow, which
|
||||
> may allow attackers to execute arbitrary JavaScript.
|
||||
|
||||
LoopBack 3 API Explorer does not support OAuth auth flow, that means
|
||||
loopback-component-explorer **IS NOT AFFECTED** by this vulnerability.
|
||||
|
||||
### GitHub advisory CVE-2019-17495
|
||||
|
||||
Link: https://github.com/advisories/GHSA-c427-hjc3-wrfw
|
||||
> A Cascading Style Sheets (CSS) injection vulnerability in Swagger UI before
|
||||
> 3.23.11 allows attackers to use the Relative Path Overwrite (RPO) technique
|
||||
> to perform CSS-based input field value exfiltration, such as exfiltration of
|
||||
> a CSRF token value.
|
||||
|
||||
Quoting from the
|
||||
[disclosure](https://github.com/tarantula-team/CSS-injection-in-Swagger-UI/tree/15edeaaa5806aa8e83ee55d883f956a3c3573ac9):
|
||||
|
||||
> We’ve observed that the `?url=` parameter in SwaggerUI allows an attacker to
|
||||
> override an otherwise hard-coded schema file. We realize that Swagger UI
|
||||
> allows users to embed untrusted Json format from remote servers This means we
|
||||
> can inject json content via the GET parameter to victim Swagger UI. etc.
|
||||
|
||||
LoopBack 3 API Explorer does not suport `?url=` parameter, it always loads the
|
||||
Swagger spec file from the LoopBack server serving the Explorer UI. That means
|
||||
loopback-component-explorer **IS NOT AFFECTED** by this vulnerability.
|
||||
|
||||
## Upgrading from v1.x
|
||||
|
||||
To upgrade your application using loopback-explorer version 1.x, just replace
|
||||
|
@ -45,7 +120,7 @@ To upgrade your application using loopback-explorer version 1.x, just replace
|
|||
var explorer = require('loopback-component-explorer'); // Module was loopback-explorer in v. 2.0.1 and earlier
|
||||
|
||||
// v1.x - does not work anymore
|
||||
app.use('/explorer', explorer(app, options);
|
||||
app.use('/explorer', explorer(app, options));
|
||||
// v2.x
|
||||
app.use('/explorer', explorer.routes(app, options));
|
||||
```
|
||||
|
@ -183,3 +258,16 @@ Options are passed to `explorer(app, options)`.
|
|||
> }
|
||||
> }
|
||||
> ```
|
||||
|
||||
## Module Long Term Support Policy
|
||||
|
||||
This module adopts the [
|
||||
Module Long Term Support (LTS)](http://github.com/CloudNativeJS/ModuleLTS) policy,
|
||||
with the following End Of Life (EOL) dates:
|
||||
|
||||
| Version | Status | Published | EOL |
|
||||
| ------- | --------------- | --------- | -------- |
|
||||
| 6.x | Maintenance LTS | Apr 2018 | Dec 2020 |
|
||||
| 5.x | End-of-Life | Sep 2017 | Dec 2019 |
|
||||
|
||||
Learn more about our LTS plan in [docs](https://loopback.io/doc/en/contrib/Long-term-support.html).
|
||||
|
|
|
@ -1,26 +1,28 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,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
|
||||
|
||||
var g = require('strong-globalize')();
|
||||
'use strict';
|
||||
|
||||
var loopback = require('loopback');
|
||||
var app = loopback();
|
||||
var explorer = require('../');
|
||||
var port = 3000;
|
||||
const g = require('strong-globalize')();
|
||||
|
||||
var User = loopback.Model.extend('user', {
|
||||
const loopback = require('loopback');
|
||||
const app = loopback();
|
||||
const explorer = require('../');
|
||||
const port = 3000;
|
||||
|
||||
const User = loopback.Model.extend('user', {
|
||||
username: 'string',
|
||||
email: 'string',
|
||||
sensitiveInternalProperty: 'string',
|
||||
}, { hidden: ['sensitiveInternalProperty'] });
|
||||
}, {hidden: ['sensitiveInternalProperty']});
|
||||
|
||||
User.attachTo(loopback.memory());
|
||||
app.model(User);
|
||||
|
||||
var apiPath = '/api';
|
||||
explorer(app, { basePath: apiPath });
|
||||
const apiPath = '/api';
|
||||
explorer(app, {basePath: apiPath});
|
||||
app.use(apiPath, loopback.rest());
|
||||
g.log('{{Explorer}} mounted at {{localhost:%s/explorer}}', port);
|
||||
|
||||
|
|
|
@ -1,25 +1,27 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// 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
|
||||
|
||||
var g = require('strong-globalize')();
|
||||
'use strict';
|
||||
|
||||
var loopback = require('loopback');
|
||||
var app = loopback();
|
||||
var explorer = require('../');
|
||||
var port = 3000;
|
||||
const g = require('strong-globalize')();
|
||||
|
||||
var Product = loopback.PersistedModel.extend('product', {
|
||||
foo: { type: 'string', required: true },
|
||||
const loopback = require('loopback');
|
||||
const app = loopback();
|
||||
const explorer = require('../');
|
||||
const port = 3000;
|
||||
|
||||
const Product = loopback.PersistedModel.extend('product', {
|
||||
foo: {type: 'string', required: true},
|
||||
bar: 'string',
|
||||
aNum: { type: 'number', min: 1, max: 10, required: true, default: 5 },
|
||||
aNum: {type: 'number', min: 1, max: 10, required: true, default: 5},
|
||||
});
|
||||
Product.attachTo(loopback.memory());
|
||||
app.model(Product);
|
||||
|
||||
var apiPath = '/api';
|
||||
explorer(app, { basePath: apiPath });
|
||||
const apiPath = '/api';
|
||||
explorer(app, {basePath: apiPath});
|
||||
app.use(apiPath, loopback.rest());
|
||||
g.log('{{Explorer}} mounted at {{http://localhost:%s/explorer}}', port);
|
||||
|
||||
|
|
34
index.js
34
index.js
|
@ -1,24 +1,24 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// 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';
|
||||
|
||||
var SG = require('strong-globalize');
|
||||
const SG = require('strong-globalize');
|
||||
SG.SetRootDir(__dirname);
|
||||
var g = SG();
|
||||
const g = SG();
|
||||
|
||||
/*!
|
||||
* Adds dynamically-updated docs as /explorer
|
||||
*/
|
||||
var url = require('url');
|
||||
var path = require('path');
|
||||
var urlJoin = require('./lib/url-join');
|
||||
var _defaults = require('lodash').defaults;
|
||||
var createSwaggerObject = require('loopback-swagger').generateSwaggerSpec;
|
||||
var SWAGGER_UI_ROOT = require('swagger-ui/index').dist;
|
||||
var STATIC_ROOT = path.join(__dirname, 'public');
|
||||
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/index').dist;
|
||||
const STATIC_ROOT = path.join(__dirname, 'public');
|
||||
|
||||
module.exports = explorer;
|
||||
explorer.routes = routes;
|
||||
|
@ -31,7 +31,7 @@ explorer.routes = routes;
|
|||
*/
|
||||
|
||||
function explorer(loopbackApplication, options) {
|
||||
options = _defaults({}, options, { mountPath: '/explorer' });
|
||||
options = _defaults({}, options, {mountPath: '/explorer'});
|
||||
loopbackApplication.use(
|
||||
options.mountPath,
|
||||
routes(loopbackApplication, options)
|
||||
|
@ -40,8 +40,8 @@ function explorer(loopbackApplication, options) {
|
|||
}
|
||||
|
||||
function routes(loopbackApplication, options) {
|
||||
var loopback = loopbackApplication.loopback;
|
||||
var loopbackMajor =
|
||||
const loopback = loopbackApplication.loopback;
|
||||
const loopbackMajor =
|
||||
(loopback && loopback.version && loopback.version.split('.')[0]) || 1;
|
||||
|
||||
if (loopbackMajor < 2) {
|
||||
|
@ -59,7 +59,7 @@ function routes(loopbackApplication, options) {
|
|||
swaggerUI: true,
|
||||
});
|
||||
|
||||
var router = new loopback.Router();
|
||||
const router = new loopback.Router();
|
||||
|
||||
mountSwagger(loopbackApplication, router, options);
|
||||
|
||||
|
@ -68,7 +68,7 @@ function routes(loopbackApplication, options) {
|
|||
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.
|
||||
var source = url.parse(req.headers.referer || '').pathname;
|
||||
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$/, '');
|
||||
|
@ -118,7 +118,7 @@ function routes(loopbackApplication, options) {
|
|||
* @param {Object} opts Options.
|
||||
*/
|
||||
function mountSwagger(loopbackApplication, swaggerApp, opts) {
|
||||
var swaggerObject = createSwaggerObject(loopbackApplication, opts);
|
||||
let swaggerObject = createSwaggerObject(loopbackApplication, opts);
|
||||
|
||||
// listening to modelRemoted event for updating the swaggerObject
|
||||
// with the newly created model to appear in the Swagger UI.
|
||||
|
@ -135,7 +135,7 @@ function mountSwagger(loopbackApplication, swaggerApp, opts) {
|
|||
// when a remote method is disabled to hide that method in the Swagger UI.
|
||||
loopbackApplication.on('remoteMethodDisabled', rebuildSwaggerObject);
|
||||
|
||||
var resourcePath = (opts && opts.resourcePath) || 'swagger.json';
|
||||
let resourcePath = (opts && opts.resourcePath) || 'swagger.json';
|
||||
if (resourcePath[0] !== '/') resourcePath = '/' + resourcePath;
|
||||
|
||||
swaggerApp.get(resourcePath, function sendSwaggerObject(req, res) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,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
|
||||
|
@ -8,6 +8,6 @@
|
|||
// Simple url joiner. Ensure we don't have to care about whether or not
|
||||
// we are fed paths with leading/trailing slashes.
|
||||
module.exports = function urlJoin() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
return args.join('/').replace(/\/+/g, '/');
|
||||
};
|
||||
|
|
12
package.json
12
package.json
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"name": "loopback-component-explorer",
|
||||
"version": "6.3.0",
|
||||
"version": "6.5.1",
|
||||
"description": "Browse and test your LoopBack app's APIs",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"node": ">=8.9"
|
||||
},
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -20,15 +20,15 @@
|
|||
"api",
|
||||
"explorer"
|
||||
],
|
||||
"author": "Ritchie Martori",
|
||||
"author": "IBM Corp.",
|
||||
"readmeFilename": "README.md",
|
||||
"bugs": {
|
||||
"url": "https://github.com/strongloop/loopback-component-explorer/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^3.2.0",
|
||||
"eslint": "^2.8.0",
|
||||
"eslint-config-loopback": "^2.0.0",
|
||||
"eslint": "^5.13.0",
|
||||
"eslint-config-loopback": "^13.0.0",
|
||||
"loopback": "^3.19.0",
|
||||
"mocha": "^5.2.0",
|
||||
"supertest": "^3.1.0"
|
||||
|
@ -36,7 +36,7 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^3.1.0",
|
||||
"lodash": "^4.17.5",
|
||||
"lodash": "^4.17.11",
|
||||
"loopback-swagger": "^5.0.0",
|
||||
"strong-globalize": "^4.1.1",
|
||||
"swagger-ui": "^2.2.5"
|
||||
|
|
|
@ -34,6 +34,7 @@ body #header a#logo {
|
|||
|
||||
body #header form#api_selector .input a#explore {
|
||||
background-color: #7dbd33 !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,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
|
||||
|
|
|
@ -1,16 +1,33 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// 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
|
||||
|
||||
var loopback = require('loopback');
|
||||
var explorer = require('../');
|
||||
var request = require('supertest');
|
||||
var assert = require('assert');
|
||||
var path = require('path');
|
||||
var expect = require('chai').expect;
|
||||
var urlJoin = require('../lib/url-join');
|
||||
var os = require('os');
|
||||
'use strict';
|
||||
|
||||
// NOTE(bajtos) It's important to run this check before we load the Explorer
|
||||
// because require() may fail (e.g. with MODULE_NOT_FOUND error) and make
|
||||
// it difficult to identify the actual problem
|
||||
const uiVersion = require('../package.json').dependencies['swagger-ui'];
|
||||
if (!uiVersion.startsWith('^2')) {
|
||||
console.error(`
|
||||
Upgrading from swagger-ui@2 to a newer major version (${uiVersion}) is difficult,
|
||||
see https://github.com/strongloop/loopback-component-explorer/issues/254
|
||||
If you are confident about this change and have manually verified API Explorer
|
||||
functionality in the browser, including access-token based authentication,
|
||||
then you can delete this check.
|
||||
`);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const loopback = require('loopback');
|
||||
const explorer = require('../');
|
||||
const request = require('supertest');
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const expect = require('chai').expect;
|
||||
const urlJoin = require('../lib/url-join');
|
||||
const os = require('os');
|
||||
|
||||
describe('explorer', function() {
|
||||
describe('with default config', function() {
|
||||
|
@ -18,7 +35,7 @@ describe('explorer', function() {
|
|||
|
||||
it('should register "loopback-component-explorer" to the app', function() {
|
||||
expect(this.app.get('loopback-component-explorer'))
|
||||
.to.have.property('mountPath', '/explorer');
|
||||
.to.have.property('mountPath', '/explorer');
|
||||
});
|
||||
|
||||
it('should redirect to /explorer/', function(done) {
|
||||
|
@ -99,7 +116,7 @@ describe('explorer', function() {
|
|||
|
||||
it('should register "loopback-component-explorer" to the app', function() {
|
||||
expect(this.app.get('loopback-component-explorer'))
|
||||
.to.have.property('mountPath', '/swagger');
|
||||
.to.have.property('mountPath', '/swagger');
|
||||
});
|
||||
|
||||
it('should serve correct swagger-ui config', function(done) {
|
||||
|
@ -120,9 +137,9 @@ describe('explorer', function() {
|
|||
|
||||
describe('with custom app.restApiRoot', function() {
|
||||
it('should serve correct swagger-ui config', function(done) {
|
||||
var app = loopback();
|
||||
const app = loopback();
|
||||
app.set('restApiRoot', '/rest-api-root');
|
||||
app.set('remoting', { cors: false });
|
||||
app.set('remoting', {cors: false});
|
||||
configureRestApiAndExplorer(app);
|
||||
|
||||
request(app)
|
||||
|
@ -142,9 +159,9 @@ describe('explorer', function() {
|
|||
// SwaggerUI builds resource URL by concatenating basePath + resourcePath
|
||||
// Since the resource paths are always startign with a slash,
|
||||
// if the basePath ends with a slash too, an incorrect URL is produced
|
||||
var app = loopback();
|
||||
const app = loopback();
|
||||
app.set('restApiRoot', '/apis/');
|
||||
app.set('remoting', { cors: false });
|
||||
app.set('remoting', {cors: false});
|
||||
configureRestApiAndExplorer(app);
|
||||
|
||||
request(app)
|
||||
|
@ -153,8 +170,8 @@ describe('explorer', function() {
|
|||
.end(function(err, res) {
|
||||
if (err) return done(err);
|
||||
|
||||
var baseUrl = res.body.basePath;
|
||||
var apiPath = Object.keys(res.body.paths)[0];
|
||||
const baseUrl = res.body.basePath;
|
||||
const apiPath = Object.keys(res.body.paths)[0];
|
||||
expect(baseUrl + apiPath).to.equal('/apis/products');
|
||||
|
||||
done();
|
||||
|
@ -163,10 +180,10 @@ describe('explorer', function() {
|
|||
});
|
||||
|
||||
describe('with custom front-end files', function() {
|
||||
var app;
|
||||
let app;
|
||||
beforeEach(function setupExplorerWithUiDirs() {
|
||||
app = loopback();
|
||||
app.set('remoting', { cors: false });
|
||||
app.set('remoting', {cors: false});
|
||||
explorer(app, {
|
||||
uiDirs: [path.resolve(__dirname, 'fixtures', 'dummy-swagger-ui')],
|
||||
});
|
||||
|
@ -189,7 +206,7 @@ describe('explorer', function() {
|
|||
request(app).get('/explorer/')
|
||||
.expect(200)
|
||||
.end(function(err, res) {
|
||||
if (err) return done(er);
|
||||
if (err) return done(err);
|
||||
// expect the content of `dummy-swagger-ui/index.html`
|
||||
expect(res.text).to.contain('custom index.html');
|
||||
done();
|
||||
|
@ -198,10 +215,10 @@ describe('explorer', function() {
|
|||
});
|
||||
|
||||
describe('with swaggerUI option', function() {
|
||||
var app;
|
||||
let app;
|
||||
beforeEach(function setupExplorerWithoutUI() {
|
||||
app = loopback();
|
||||
app.set('remoting', { cors: false });
|
||||
app.set('remoting', {cors: false});
|
||||
explorer(app, {
|
||||
swaggerUI: false,
|
||||
});
|
||||
|
@ -234,11 +251,11 @@ describe('explorer', function() {
|
|||
});
|
||||
|
||||
describe('explorer.routes API', function() {
|
||||
var app;
|
||||
let app;
|
||||
beforeEach(function() {
|
||||
app = loopback();
|
||||
app.set('remoting', { cors: false });
|
||||
var Product = loopback.PersistedModel.extend('product');
|
||||
app.set('remoting', {cors: false});
|
||||
const Product = loopback.PersistedModel.extend('product');
|
||||
Product.attachTo(loopback.memory());
|
||||
app.model(Product);
|
||||
});
|
||||
|
@ -256,10 +273,10 @@ describe('explorer', function() {
|
|||
});
|
||||
|
||||
describe('when specifying custom static file root directories', function() {
|
||||
var app;
|
||||
let app;
|
||||
beforeEach(function() {
|
||||
app = loopback();
|
||||
app.set('remoting', { cors: false });
|
||||
app.set('remoting', {cors: false});
|
||||
});
|
||||
|
||||
it('should allow `uiDirs` to be defined as an Array', function(done) {
|
||||
|
@ -294,8 +311,8 @@ describe('explorer', function() {
|
|||
});
|
||||
|
||||
it('updates swagger object when a new model is added', function(done) {
|
||||
var app = loopback();
|
||||
app.set('remoting', { cors: false });
|
||||
const app = loopback();
|
||||
app.set('remoting', {cors: false});
|
||||
configureRestApiAndExplorer(app, '/explorer');
|
||||
|
||||
// Ensure the swagger object was built
|
||||
|
@ -306,7 +323,7 @@ describe('explorer', function() {
|
|||
if (err) return done(err);
|
||||
|
||||
// Create a new model
|
||||
var Model = loopback.PersistedModel.extend('Customer');
|
||||
const Model = loopback.PersistedModel.extend('Customer');
|
||||
Model.attachTo(loopback.memory());
|
||||
app.model(Model);
|
||||
|
||||
|
@ -317,9 +334,9 @@ describe('explorer', function() {
|
|||
.end(function(err, res) {
|
||||
if (err) return done(err);
|
||||
|
||||
var modelNames = Object.keys(res.body.definitions);
|
||||
const modelNames = Object.keys(res.body.definitions);
|
||||
expect(modelNames).to.contain('Customer');
|
||||
var paths = Object.keys(res.body.paths);
|
||||
const paths = Object.keys(res.body.paths);
|
||||
expect(paths).to.have.contain('/Customers');
|
||||
|
||||
done();
|
||||
|
@ -328,11 +345,11 @@ describe('explorer', function() {
|
|||
});
|
||||
|
||||
it('updates swagger object when a model is removed', function(done) {
|
||||
var app = loopback();
|
||||
app.set('remoting', { cors: false });
|
||||
const app = loopback();
|
||||
app.set('remoting', {cors: false});
|
||||
configureRestApiAndExplorer(app, '/explorer');
|
||||
|
||||
var Model = loopback.PersistedModel.extend('Customer');
|
||||
const Model = loopback.PersistedModel.extend('Customer');
|
||||
Model.attachTo(loopback.memory());
|
||||
app.model(Model);
|
||||
|
||||
|
@ -352,9 +369,9 @@ describe('explorer', function() {
|
|||
.end(function(err, res) {
|
||||
if (err) return done(err);
|
||||
|
||||
var modelNames = Object.keys(res.body.definitions);
|
||||
const modelNames = Object.keys(res.body.definitions);
|
||||
expect(modelNames).to.not.contain('Customer');
|
||||
var paths = Object.keys(res.body.paths);
|
||||
const paths = Object.keys(res.body.paths);
|
||||
expect(paths).to.not.contain('/Customers');
|
||||
|
||||
done();
|
||||
|
@ -363,8 +380,8 @@ describe('explorer', function() {
|
|||
});
|
||||
|
||||
it('updates swagger object when a remote method is disabled', function(done) {
|
||||
var app = loopback();
|
||||
app.set('remoting', { cors: false });
|
||||
const app = loopback();
|
||||
app.set('remoting', {cors: false});
|
||||
configureRestApiAndExplorer(app, '/explorer');
|
||||
|
||||
// Ensure the swagger object was built
|
||||
|
@ -375,10 +392,10 @@ describe('explorer', function() {
|
|||
if (err) return done(err);
|
||||
|
||||
// Check the method that will be disabled
|
||||
var paths = Object.keys(res.body.paths);
|
||||
const paths = Object.keys(res.body.paths);
|
||||
expect(paths).to.contain('/products/findOne');
|
||||
|
||||
var Product = app.models.Product;
|
||||
const Product = app.models.Product;
|
||||
Product.disableRemoteMethodByName('findOne');
|
||||
|
||||
// Request swagger.json again
|
||||
|
@ -388,7 +405,7 @@ describe('explorer', function() {
|
|||
.end(function(err, res) {
|
||||
if (err) return done(err);
|
||||
|
||||
var paths = Object.keys(res.body.paths);
|
||||
const paths = Object.keys(res.body.paths);
|
||||
expect(paths).to.not.contain('/products/findOne');
|
||||
|
||||
done();
|
||||
|
@ -397,8 +414,8 @@ describe('explorer', function() {
|
|||
});
|
||||
|
||||
it('updates swagger object when a remote method is added', function(done) {
|
||||
var app = loopback();
|
||||
app.set('remoting', { cors: false });
|
||||
const app = loopback();
|
||||
app.set('remoting', {cors: false});
|
||||
configureRestApiAndExplorer(app, '/explorer');
|
||||
|
||||
// Ensure the swagger object was built
|
||||
|
@ -409,10 +426,10 @@ describe('explorer', function() {
|
|||
if (err) return done(err);
|
||||
|
||||
// Check the method that will be disabled
|
||||
var paths = Object.keys(res.body.paths);
|
||||
const paths = Object.keys(res.body.paths);
|
||||
expect(paths).to.contain('/products/findOne');
|
||||
|
||||
var Product = app.models.Product;
|
||||
const Product = app.models.Product;
|
||||
Product.findOne2 = function(cb) { cb(null, 1); };
|
||||
Product.remoteMethod('findOne2', {});
|
||||
|
||||
|
@ -423,7 +440,7 @@ describe('explorer', function() {
|
|||
.end(function(err, res) {
|
||||
if (err) return done(err);
|
||||
|
||||
var paths = Object.keys(res.body.paths);
|
||||
const paths = Object.keys(res.body.paths);
|
||||
expect(paths).to.contain('/products/findOne2');
|
||||
|
||||
done();
|
||||
|
@ -433,8 +450,8 @@ describe('explorer', function() {
|
|||
|
||||
function givenLoopBackAppWithExplorer(explorerBase) {
|
||||
return function(done) {
|
||||
var app = this.app = loopback();
|
||||
app.set('remoting', { cors: false });
|
||||
const app = this.app = loopback();
|
||||
app.set('remoting', {cors: false});
|
||||
configureRestApiAndExplorer(app, explorerBase);
|
||||
|
||||
done();
|
||||
|
@ -442,11 +459,11 @@ describe('explorer', function() {
|
|||
}
|
||||
|
||||
function configureRestApiAndExplorer(app, explorerBase) {
|
||||
var Product = loopback.PersistedModel.extend('product');
|
||||
const Product = loopback.PersistedModel.extend('product');
|
||||
Product.attachTo(loopback.memory());
|
||||
app.model(Product);
|
||||
|
||||
explorer(app, { mountPath: explorerBase });
|
||||
explorer(app, {mountPath: explorerBase});
|
||||
app.set('legacyExplorer', false);
|
||||
app.use(app.get('restApiRoot') || '/', loopback.rest());
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,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
|
||||
|
|
Loading…
Reference in New Issue