diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ed649b0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,151 @@ +### Contributing ### + +Thank you for your interest in `loopback-explorer`, an open source project +administered by StrongLoop. + +Contributing to `loopback-explorer` is easy. In a few simple steps: + + * Ensure that your effort is aligned with the project's roadmap by + talking to the maintainers, especially if you are going to spend a + lot of time on it. + + * Make something better or fix a bug. + + * Adhere to code style outlined in the [Google C++ Style Guide][] and + [Google Javascript Style Guide][]. + + * Sign the [Contributor License Agreement](https://cla.strongloop.com/strongloop/loopback-explorer) + + * Submit a pull request through Github. + + +### Contributor License Agreement ### + +``` + Individual Contributor License Agreement + + By signing this Individual Contributor License Agreement + ("Agreement"), and making a Contribution (as defined below) to + StrongLoop, Inc. ("StrongLoop"), You (as defined below) accept and + agree to the following terms and conditions for Your present and + future Contributions submitted to StrongLoop. Except for the license + granted in this Agreement to StrongLoop and recipients of software + distributed by StrongLoop, You reserve all right, title, and interest + in and to Your Contributions. + + 1. Definitions + + "You" or "Your" shall mean the copyright owner or the individual + authorized by the copyright owner that is entering into this + Agreement with StrongLoop. + + "Contribution" shall mean any original work of authorship, + including any modifications or additions to an existing work, that + is intentionally submitted by You to StrongLoop for inclusion in, + or documentation of, any of the products owned or managed by + StrongLoop ("Work"). For purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication + sent to StrongLoop or its representatives, including but not + limited to communication or electronic mailing lists, source code + control systems, and issue tracking systems that are managed by, + or on behalf of, StrongLoop for the purpose of discussing and + improving the Work, but excluding communication that is + conspicuously marked or otherwise designated in writing by You as + "Not a Contribution." + + 2. You Grant a Copyright License to StrongLoop + + Subject to the terms and conditions of this Agreement, You hereby + grant to StrongLoop and recipients of software distributed by + StrongLoop, a perpetual, worldwide, non-exclusive, no-charge, + royalty-free, irrevocable copyright license to reproduce, prepare + derivative works of, publicly display, publicly perform, + sublicense, and distribute Your Contributions and such derivative + works under any license and without any restrictions. + + 3. You Grant a Patent License to StrongLoop + + Subject to the terms and conditions of this Agreement, You hereby + grant to StrongLoop and to recipients of software distributed by + StrongLoop a perpetual, worldwide, non-exclusive, no-charge, + royalty-free, irrevocable (except as stated in this Section) + patent license to make, have made, use, offer to sell, sell, + import, and otherwise transfer the Work under any license and + without any restrictions. The patent license You grant to + StrongLoop under this Section applies only to those patent claims + licensable by You that are necessarily infringed by Your + Contributions(s) alone or by combination of Your Contributions(s) + with the Work to which such Contribution(s) was submitted. If any + entity institutes a patent litigation against You or any other + entity (including a cross-claim or counterclaim in a lawsuit) + alleging that Your Contribution, or the Work to which You have + contributed, constitutes direct or contributory patent + infringement, any patent licenses granted to that entity under + this Agreement for that Contribution or Work shall terminate as + of the date such litigation is filed. + + 4. You Have the Right to Grant Licenses to StrongLoop + + You represent that You are legally entitled to grant the licenses + in this Agreement. + + If Your employer(s) has rights to intellectual property that You + create, You represent that You have received permission to make + the Contributions on behalf of that employer, that Your employer + has waived such rights for Your Contributions, or that Your + employer has executed a separate Corporate Contributor License + Agreement with StrongLoop. + + 5. The Contributions Are Your Original Work + + You represent that each of Your Contributions are Your original + works of authorship (see Section 8 (Submissions on Behalf of + Others) for submission on behalf of others). You represent that to + Your knowledge, no other person claims, or has the right to claim, + any right in any intellectual property right related to Your + Contributions. + + You also represent that You are not legally obligated, whether by + entering into an agreement or otherwise, in any way that conflicts + with the terms of this Agreement. + + You represent that Your Contribution submissions include complete + details of any third-party license or other restriction (including, + but not limited to, related patents and trademarks) of which You + are personally aware and which are associated with any part of + Your Contributions. + + 6. You Don't Have an Obligation to Provide Support for Your Contributions + + You are not expected to provide support for Your Contributions, + except to the extent You desire to provide support. You may provide + support for free, for a fee, or not at all. + + 6. No Warranties or Conditions + + StrongLoop acknowledges that unless required by applicable law or + agreed to in writing, You provide Your Contributions on an "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER + EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES + OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR + FITNESS FOR A PARTICULAR PURPOSE. + + 7. Submission on Behalf of Others + + If You wish to submit work that is not Your original creation, You + may submit it to StrongLoop separately from any Contribution, + identifying the complete details of its source and of any license + or other restriction (including, but not limited to, related + patents, trademarks, and license agreements) of which You are + personally aware, and conspicuously marking the work as + "Submitted on Behalf of a Third-Party: [named here]". + + 8. Agree to Notify of Change of Circumstances + + You agree to notify StrongLoop of any facts or circumstances of + which You become aware that would make these representations + inaccurate in any respect. Email us at callback@strongloop.com. +``` + +[Google C++ Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml +[Google Javascript Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml diff --git a/lib/swagger.js b/lib/swagger.js index bcf3e07..7b5644c 100644 --- a/lib/swagger.js +++ b/lib/swagger.js @@ -12,6 +12,7 @@ var urlJoin = require('./url-join'); var _defaults = require('lodash.defaults'); var classHelper = require('./class-helper'); var routeHelper = require('./route-helper'); +var cors = require('cors'); /** * Create a remotable Swagger module for plugging into `RemoteObjects`. @@ -38,6 +39,8 @@ function Swagger(loopbackApplication, swaggerApp, opts) { var routes = adapter.allRoutes(); var classes = remotes.classes(); + setupCors(swaggerApp, remotes); + // These are the docs we will be sending from the /swagger endpoints. var resourceDoc = generateResourceDoc(opts); var apiDocs = {}; @@ -70,13 +73,20 @@ function Swagger(loopbackApplication, swaggerApp, opts) { }); /** - * The topmost Swagger resource is a description of all (non-Swagger) - * resources available on the system, and where to find more + * The topmost Swagger resource is a description of all (non-Swagger) + * resources available on the system, and where to find more * information about them. */ addRoute(swaggerApp, opts.resourcePath, resourceDoc, opts); } +function setupCors(swaggerApp, remotes) { + var corsOptions = remotes.options && remotes.options.cors || + { origin: true, credentials: true }; + + swaggerApp.use(cors(corsOptions)); +} + /** * Add a route to this remoting extension. * @param {Application} app Express application. diff --git a/package.json b/package.json index e1cde4a..fea26bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-explorer", - "version": "1.2.11", + "version": "1.3.0", "description": "Browse and test your LoopBack app's APIs", "main": "index.js", "scripts": { @@ -31,10 +31,11 @@ "url": "https://github.com/strongloop/loopback-explorer/blob/master/LICENSE" }, "dependencies": { - "swagger-ui": "~2.0.18", + "cors": "^2.4.2", "debug": "~1.0.3", + "express": "3.x", "lodash.clonedeep": "^2.4.1", "lodash.defaults": "^2.4.1", - "express": "3.x" + "swagger-ui": "~2.0.18" } } diff --git a/public/css/loopbackStyles.css b/public/css/loopbackStyles.css index 8065c59..04d6894 100644 --- a/public/css/loopbackStyles.css +++ b/public/css/loopbackStyles.css @@ -21,6 +21,19 @@ bottom: -30px; } +/* JSON syntax highlighting */ +.json, .json .attribute { + color: black; +} + +.json .value .string { + color: #800; +} + +.json .value .number, .json .value .literal { + color: #080; +} + /* FIXME: Separate the overrides from the rest of the styles, rather than override screen.css entirely. */ diff --git a/public/lib/loadSwaggerUI.js b/public/lib/loadSwaggerUI.js index 241d335..c89917b 100644 --- a/public/lib/loadSwaggerUI.js +++ b/public/lib/loadSwaggerUI.js @@ -26,7 +26,9 @@ $(function() { log('Unable to Load SwaggerUI'); log(data); }, - docExpansion: 'none' + docExpansion: 'none', + highlightSizeThreshold: 16384, + sorter: 'alpha' }); $('#explore').click(setAccessToken); diff --git a/test/swagger.test.js b/test/swagger.test.js index 1cd0a5b..b07f0bb 100644 --- a/test/swagger.test.js +++ b/test/swagger.test.js @@ -106,6 +106,31 @@ describe('swagger definition', function() { }); }); + describe('Cross-origin resource sharing', function() { + it('allows cross-origin requests by default', function(done) { + var app = mountSwagger(); + request(app) + .options('/explorer/resources') + .set('Origin', 'http://example.com/') + .expect('Access-Control-Allow-Origin', /^http:\/\/example.com\/|\*/) + .expect('Access-Control-Allow-Methods', /\bGET\b/) + .end(done); + }); + + it('can be disabled by configuration', function(done) { + var app = mountSwagger({}, { remoting: { cors: { origin: false } } }); + request(app) + .options('/explorer/resources') + .end(function(err, res) { + if (err) return done(err); + var allowOrigin = res.get('Access-Control-Allow-Origin'); + expect(allowOrigin, 'Access-Control-Allow-Origin') + .to.equal(undefined); + done(); + }); + }); + }); + function getSwaggerResources(app, restPath, classPath) { return request(app) .get(urlJoin(restPath || '/explorer', '/resources', classPath || '')) @@ -122,6 +147,7 @@ describe('swagger definition', function() { addlOptions = addlOptions || {}; var app = createLoopbackAppWithModel(addlOptions.apiRoot); var swaggerApp = express(); + if (addlOptions.remoting) app.set('remoting', addlOptions.remoting); swagger(app, swaggerApp, options); app.use(addlOptions.explorerRoot || '/explorer', swaggerApp); return app;