From bf3a2c9764392fe5d88cbef77f31d1bf37d0e88f Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Mon, 4 Aug 2014 23:22:41 -0700 Subject: [PATCH 01/14] Fix the type name for a property if model class is used See https://github.com/strongloop/loopback-explorer/issues/32 --- lib/model-helper.js | 4 +++- test/model-helper.test.js | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/model-helper.js b/lib/model-helper.js index e36ce6f..63f6ca0 100644 --- a/lib/model-helper.js +++ b/lib/model-helper.js @@ -83,7 +83,9 @@ var modelHelper = module.exports = { */ getPropType: function getPropType(propType) { if (typeof propType === 'function') { - propType = propType.name.toLowerCase(); + // See https://github.com/strongloop/loopback-explorer/issues/32 + // The type can be a model class + propType = propType.modelName || propType.name.toLowerCase(); } else if(Array.isArray(propType)) { propType = 'array'; } diff --git a/test/model-helper.test.js b/test/model-helper.test.js index 0676767..e376a2a 100644 --- a/test/model-helper.test.js +++ b/test/model-helper.test.js @@ -1,6 +1,7 @@ 'use strict'; var modelHelper = require('../lib/model-helper'); +var loopback = require('loopback'); var expect = require('chai').expect; describe('model-helper', function() { @@ -109,6 +110,17 @@ describe('model-helper', function() { var prop = def.properties.array; expect(prop).to.eql({ type: 'array', items: { type: 'any' } }); }); + + it('converts Model type', function() { + var Address = loopback.createModel('Address', {street: String}); + var def = buildSwaggerModels({ + str: String, + address: Address + }); + var prop = def.properties.address; + expect(prop).to.eql({ type: 'Address' }); + }); + }); }); describe('related models', function() { From b1fc7347ce4fb63005fcd8deb2bc4fddd361fb0c Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 8 Aug 2014 11:21:42 -0700 Subject: [PATCH 02/14] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 62ccc93..c5a0831 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-explorer", - "version": "1.2.7", + "version": "1.2.8", "description": "Browse and test your LoopBack app's APIs", "main": "index.js", "scripts": { From bda9e5f62766c53848085e141b9765a001542ca9 Mon Sep 17 00:00:00 2001 From: Navid Nikpour Date: Sun, 10 Aug 2014 08:46:24 +0200 Subject: [PATCH 03/14] Fixed undefined modelClass when using polymorphic relations --- lib/model-helper.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/model-helper.js b/lib/model-helper.js index 63f6ca0..5820550 100644 --- a/lib/model-helper.js +++ b/lib/model-helper.js @@ -67,7 +67,9 @@ var modelHelper = module.exports = { // Generate model definitions for related models for (var r in modelClass.relations) { var rel = modelClass.relations[r]; - generateModelDefinition(rel.modelTo, out); + if (rel.modelTo){ + generateModelDefinition(rel.modelTo, out); + } if (rel.modelThrough) { generateModelDefinition(rel.modelThrough, out); } From e7efa82640440664d67e289698c0aa58c0c38d6b Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Tue, 12 Aug 2014 09:47:53 -0400 Subject: [PATCH 04/14] Use `dist` property from swagger-ui package. This removes the hardcoded local `node_modules` lookup for swagger-ui, which will break if the parent installs swagger-ui itself. This is useful if, for example, the latest swagger-ui breaks loopback-explorer, and the parent project wants to install a fixed version in its root. --- index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/index.js b/index.js index 5062627..6c5e003 100644 --- a/index.js +++ b/index.js @@ -8,8 +8,7 @@ var urlJoin = require('./lib/url-join'); var _defaults = require('lodash.defaults'); var express = require('express'); var swagger = require('./lib/swagger'); -var SWAGGER_UI_ROOT = path.join(__dirname, 'node_modules', - 'swagger-ui', 'dist'); +var SWAGGER_UI_ROOT = require('swagger-ui').dist; var STATIC_ROOT = path.join(__dirname, 'public'); module.exports = explorer; From d98a7877d46dccb036c7e653e548673f39f7829f Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Tue, 12 Aug 2014 12:44:16 -0400 Subject: [PATCH 05/14] Newest Swagger UI requires application/x-www-form-urlencoded. UI will fail to POST if this consumes type is not specified. --- lib/swagger.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/swagger.js b/lib/swagger.js index d517303..bcf3e07 100644 --- a/lib/swagger.js +++ b/lib/swagger.js @@ -26,8 +26,8 @@ function Swagger(loopbackApplication, swaggerApp, opts) { swaggerVersion: '1.2', basePath: loopbackApplication.get('restApiRoot') || '/api', resourcePath: 'resources', - // Default consumes/produces to application/json - consumes: ['application/json'], + // Default consumes/produces + consumes: ['application/json', 'application/x-www-form-urlencoded'], produces: ['application/json'], version: getVersion() }); From 937ec98c97b1965828896ba0d2529ff6bf484765 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 15 Aug 2014 23:37:10 -0700 Subject: [PATCH 06/14] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c5a0831..abe35aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-explorer", - "version": "1.2.8", + "version": "1.2.9", "description": "Browse and test your LoopBack app's APIs", "main": "index.js", "scripts": { From d083feab367139ee82e106e6a05463709478aa91 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Thu, 28 Aug 2014 15:20:03 -0700 Subject: [PATCH 07/14] Make sure nested/referenced models are mapped to swagger --- lib/model-helper.js | 23 +++++++++++++++++++++++ test/model-helper.test.js | 25 ++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/model-helper.js b/lib/model-helper.js index 5820550..e77e181 100644 --- a/lib/model-helper.js +++ b/lib/model-helper.js @@ -29,6 +29,19 @@ var modelHelper = module.exports = { // Don't modify original properties. var properties = _cloneDeep(def.properties); + var referencedModels = []; + // Add models from settings + if (def.settings && def.settings.models) { + for (var m in def.settings.models) { + var model = modelClass[m]; + if (typeof model === 'function' && model.modelName) { + if (referencedModels.indexOf(model) === -1) { + referencedModels.push(model); + } + } + } + } + // Iterate through each property in the model definition. // Types may be defined as constructors (e.g. String, Date, etc.), // or as strings; getPropType() will take care of the conversion. @@ -56,6 +69,13 @@ var modelHelper = module.exports = { // Assign this back to the properties object. properties[key] = prop; + + var propType = def.properties[key].type; + if (typeof propType === 'function' && propType.modelName) { + if (referencedModels.indexOf(m) === -1) { + referencedModels.push(propType); + } + } }); out[name] = { @@ -74,6 +94,9 @@ var modelHelper = module.exports = { generateModelDefinition(rel.modelThrough, out); } } + for(var rm in referencedModels) { + generateModelDefinition(referencedModels[rm], out); + } return out; }, diff --git a/test/model-helper.test.js b/test/model-helper.test.js index e376a2a..0686818 100644 --- a/test/model-helper.test.js +++ b/test/model-helper.test.js @@ -123,15 +123,38 @@ describe('model-helper', function() { }); }); + describe('related models', function() { - it('should include related models', function() { + it('should include related models', function () { var defs = buildSwaggerModelsWithRelations({ str: String // 'string' }); expect(defs).has.property('testModel'); expect(defs).has.property('relatedModel'); }); + + it('should include nesting models', function() { + var Model2 = loopback.createModel('Model2', {street: String}); + var Model1 = loopback.createModel('Model1', { + str: String, // 'string' + address: Model2 + }); + var defs = modelHelper.generateModelDefinition(Model1, {}); + expect(defs).has.property('Model1'); + expect(defs).has.property('Model2'); + }); + + it('should include used models', function() { + var Model4 = loopback.createModel('Model4', {street: String}); + var Model3 = loopback.createModel('Model3', { + str: String, // 'string' + }, {models: {model4: 'Model4'}}); + var defs = modelHelper.generateModelDefinition(Model3, {}); + expect(defs).has.property('Model3'); + expect(defs).has.property('Model4'); + }); }); + describe('hidden properties', function() { it('should hide properties marked as "hidden"', function() { var aClass = createModelCtor({ From acf00f325405c4ed4c538dbde25f2e0f17dd956f Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Fri, 29 Aug 2014 16:06:05 +0800 Subject: [PATCH 08/14] Make sure nested/referenced models in array are mapped to swagger Signed-off-by: Clark Wang --- lib/model-helper.js | 10 +++++++++- test/model-helper.test.js | 11 +++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/model-helper.js b/lib/model-helper.js index e77e181..1fafcbd 100644 --- a/lib/model-helper.js +++ b/lib/model-helper.js @@ -72,10 +72,18 @@ var modelHelper = module.exports = { var propType = def.properties[key].type; if (typeof propType === 'function' && propType.modelName) { - if (referencedModels.indexOf(m) === -1) { + if (referencedModels.indexOf(propType) === -1) { referencedModels.push(propType); } } + if (Array.isArray(propType) && propType.length) { + var itemType = propType[0]; + if (typeof itemType === 'function' && itemType.modelName) { + if (referencedModels.indexOf(itemType) === -1) { + referencedModels.push(itemType); + } + } + } }); out[name] = { diff --git a/test/model-helper.test.js b/test/model-helper.test.js index 0686818..efe720a 100644 --- a/test/model-helper.test.js +++ b/test/model-helper.test.js @@ -153,6 +153,17 @@ describe('model-helper', function() { expect(defs).has.property('Model3'); expect(defs).has.property('Model4'); }); + + it('should include nesting models in array', function() { + var Model6 = loopback.createModel('Model6', {street: String}); + var Model5 = loopback.createModel('Model5', { + str: String, // 'string' + addresses: [Model6] + }); + var defs = modelHelper.generateModelDefinition(Model5, {}); + expect(defs).has.property('Model5'); + expect(defs).has.property('Model6'); + }); }); describe('hidden properties', function() { From 91caa92be735a75ede6175f1a864b484471c33b5 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 5 Sep 2014 15:44:50 -0700 Subject: [PATCH 09/14] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index abe35aa..9bef826 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-explorer", - "version": "1.2.9", + "version": "1.2.10", "description": "Browse and test your LoopBack app's APIs", "main": "index.js", "scripts": { From 282b70e1e464e9c593f48af05d518554df8105b9 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 19 Sep 2014 09:35:11 -0700 Subject: [PATCH 10/14] Fix how the array of models is iterated https://github.com/strongloop/loopback-explorer/issues/49 --- lib/model-helper.js | 4 ++-- test/model-helper.test.js | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/model-helper.js b/lib/model-helper.js index 1fafcbd..1cff049 100644 --- a/lib/model-helper.js +++ b/lib/model-helper.js @@ -102,8 +102,8 @@ var modelHelper = module.exports = { generateModelDefinition(rel.modelThrough, out); } } - for(var rm in referencedModels) { - generateModelDefinition(referencedModels[rm], out); + for (var i = 0, n = referencedModels.length; i < n; i++) { + generateModelDefinition(referencedModels[i], out); } return out; }, diff --git a/test/model-helper.test.js b/test/model-helper.test.js index efe720a..98fd4b1 100644 --- a/test/model-helper.test.js +++ b/test/model-helper.test.js @@ -164,6 +164,18 @@ describe('model-helper', function() { expect(defs).has.property('Model5'); expect(defs).has.property('Model6'); }); + + // https://github.com/strongloop/loopback-explorer/issues/49 + it('should work if Array class is extended and no related models are found', + function() { + var Model7 = loopback.createModel('Model7', {street: String}); + Array.prototype.customFunc = function() { + }; + var defs = modelHelper.generateModelDefinition(Model7, {}); + expect(defs).has.property('Model7'); + expect(Object.keys(defs)).has.property('length', 1); + }); + }); describe('hidden properties', function() { From b488fa3c2563f7822a9fc7ab680e9421c118736f Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Mon, 22 Sep 2014 09:04:47 -0700 Subject: [PATCH 11/14] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9bef826..e1cde4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-explorer", - "version": "1.2.10", + "version": "1.2.11", "description": "Browse and test your LoopBack app's APIs", "main": "index.js", "scripts": { From 64c25a8c3c12f32a0eed2bbe18672da86aa76aab Mon Sep 17 00:00:00 2001 From: Ryan Graham Date: Wed, 1 Oct 2014 17:55:03 -0700 Subject: [PATCH 12/14] Add contribution guidelines Uses https://cla.strongloop.com --- CONTRIBUTING.md | 151 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 CONTRIBUTING.md 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 From ad68695009201a2a0bf2abf3eef6cc8d1c1d11c5 Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Tue, 7 Oct 2014 11:07:41 +0200 Subject: [PATCH 13/14] Add syntax highlighting styles & highlight threshold. Previously, highlighting classes were being added to the DOM but had no CSS so the user just saw black - a lot of CPU usage for nothing. These styles now attractively and minimally style resposes so long as they are below a certain threshold, as well as model schemas. The highlightSizeThreshold fixes the long hang users would see when large responses were returned. --- public/css/loopbackStyles.css | 13 +++++++++++++ public/lib/loadSwaggerUI.js | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) 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..32c02fd 100644 --- a/public/lib/loadSwaggerUI.js +++ b/public/lib/loadSwaggerUI.js @@ -26,7 +26,8 @@ $(function() { log('Unable to Load SwaggerUI'); log(data); }, - docExpansion: 'none' + docExpansion: 'none', + highlightSizeThreshold: 16384 }); $('#explore').click(setAccessToken); From 0a35cdb4af4c1f602e884e2e5ba68f64e0bd489b Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Tue, 7 Oct 2014 11:12:47 +0200 Subject: [PATCH 14/14] Sort endpoints by letter. Merged from swagger-ui's index.html. Previously, endpoints were sorted in the order they were defined. --- public/lib/loadSwaggerUI.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/lib/loadSwaggerUI.js b/public/lib/loadSwaggerUI.js index 32c02fd..c89917b 100644 --- a/public/lib/loadSwaggerUI.js +++ b/public/lib/loadSwaggerUI.js @@ -27,7 +27,8 @@ $(function() { log(data); }, docExpansion: 'none', - highlightSizeThreshold: 16384 + highlightSizeThreshold: 16384, + sorter: 'alpha' }); $('#explore').click(setAccessToken);