├── index.js ├── .eslintignore ├── .gitignore ├── _doc.scss ├── lib ├── nodejs │ ├── swagger-to-html │ │ ├── __tests__ │ │ │ ├── templates │ │ │ │ ├── body.ejs │ │ │ │ ├── head.ejs │ │ │ │ ├── includes │ │ │ │ │ ├── description.ejs │ │ │ │ │ └── subtitle.ejs │ │ │ │ ├── head-include-module.ejs │ │ │ │ └── head-include-module-doesnt-exist.ejs │ │ │ ├── index.test.js │ │ │ ├── _petstore.yaml │ │ │ └── core.test.js │ │ ├── helpers │ │ │ ├── parser │ │ │ │ ├── index.js │ │ │ │ ├── lib │ │ │ │ │ ├── init.js │ │ │ │ │ ├── processItems │ │ │ │ │ │ ├── default.js │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ ├── enum.js │ │ │ │ │ │ ├── array.js │ │ │ │ │ │ └── object.js │ │ │ │ │ ├── parseBody.js │ │ │ │ │ ├── parseResponse.js │ │ │ │ │ ├── updateResult.js │ │ │ │ │ └── traverse.js │ │ │ │ └── __tests__ │ │ │ │ │ ├── index.test.js │ │ │ │ │ └── lib │ │ │ │ │ ├── processItems │ │ │ │ │ ├── index.test.js │ │ │ │ │ ├── default.test.js │ │ │ │ │ ├── enum.test.js │ │ │ │ │ ├── array.test.js │ │ │ │ │ └── object.test.js │ │ │ │ │ ├── init.test.js │ │ │ │ │ ├── parseBody.test.js │ │ │ │ │ ├── parseResponse.test.js │ │ │ │ │ ├── updateResult.test.js │ │ │ │ │ └── traverse.test.js │ │ │ ├── index.js │ │ │ └── utils.js │ │ ├── modules │ │ │ ├── templates │ │ │ │ ├── operations.ejs │ │ │ │ ├── responseSample.ejs │ │ │ │ ├── requestSample.ejs │ │ │ │ ├── head.ejs │ │ │ │ ├── requestContent.ejs │ │ │ │ ├── security.ejs │ │ │ │ ├── securityRequirement.ejs │ │ │ │ ├── responses.ejs │ │ │ │ ├── response.ejs │ │ │ │ ├── operation.ejs │ │ │ │ ├── formData.ejs │ │ │ │ ├── emptyResponse.ejs │ │ │ │ ├── request.ejs │ │ │ │ ├── header.ejs │ │ │ │ ├── responseBody.ejs │ │ │ │ ├── requestParams.ejs │ │ │ │ ├── requestBody.ejs │ │ │ │ └── securityDefinition.ejs │ │ │ ├── controllers │ │ │ │ ├── security.js │ │ │ │ ├── responses.js │ │ │ │ ├── requestBody.js │ │ │ │ ├── operation.js │ │ │ │ ├── __tests__ │ │ │ │ │ ├── responses.test.js │ │ │ │ │ ├── security.test.js │ │ │ │ │ ├── index.test.js │ │ │ │ │ ├── requestBody.test.js │ │ │ │ │ ├── response.test.js │ │ │ │ │ ├── responseSample.test.js │ │ │ │ │ ├── operation.test.js │ │ │ │ │ ├── operations.test.js │ │ │ │ │ ├── request.test.js │ │ │ │ │ └── requestSample.test.js │ │ │ │ ├── response.js │ │ │ │ ├── responseSample.js │ │ │ │ ├── index.js │ │ │ │ ├── request.js │ │ │ │ ├── operations.js │ │ │ │ └── requestSample.js │ │ │ └── index.js │ │ └── index.js │ ├── .eslintrc │ ├── search │ │ ├── child.js │ │ ├── child-on-message.js │ │ ├── __tests__ │ │ │ ├── mocks.js │ │ │ ├── child-on-message.test.js │ │ │ ├── build.test.js │ │ │ ├── searcher.test.js │ │ │ └── generator.test.js │ │ ├── searcher.js │ │ ├── generator.js │ │ └── build.js │ ├── swagger │ │ ├── decorators │ │ │ ├── index.js │ │ │ ├── __tests__ │ │ │ │ ├── index.test.js │ │ │ │ └── host.test.js │ │ │ ├── docExclude │ │ │ │ ├── filters │ │ │ │ │ ├── index.js │ │ │ │ │ ├── paths.js │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ ├── index.test.js │ │ │ │ │ │ ├── paths.test.js │ │ │ │ │ │ ├── operations.test.js │ │ │ │ │ │ ├── parameters.test.js │ │ │ │ │ │ └── security.test.js │ │ │ │ │ ├── operations.js │ │ │ │ │ ├── parameters.js │ │ │ │ │ └── security.js │ │ │ │ ├── index.js │ │ │ │ ├── utils.js │ │ │ │ └── __tests__ │ │ │ │ │ └── index.test.js │ │ │ └── host.js │ │ ├── index.js │ │ └── __tests__ │ │ │ ├── index.test.js │ │ │ ├── swagger-unmocked.test.js │ │ │ └── swagger-mocks.js │ ├── swagger-ui │ │ ├── parse-schema-file-error.js │ │ ├── partials │ │ │ ├── v2 │ │ │ │ ├── libs.ejs │ │ │ │ └── snippet.ejs │ │ │ └── v3 │ │ │ │ ├── snippet.ejs │ │ │ │ └── libs.ejs │ │ ├── parse-schema-file.js │ │ └── __tests__ │ │ │ ├── mocks.js │ │ │ └── parse-schema-file.test.js │ ├── support │ │ ├── index.js │ │ └── __tests__ │ │ │ └── index.test.js │ ├── hexo-util.js │ ├── __tests__ │ │ ├── hexo-util.test.js │ │ └── swagger-store.test.js │ ├── project-partial │ │ ├── index.js │ │ └── __tests__ │ │ │ └── index.test.js │ └── swagger-store.js └── browser │ ├── polyfills.js │ ├── search │ ├── actions.js │ ├── load.js │ ├── __tests__ │ │ ├── load.test.js │ │ ├── containers.test.jsx │ │ └── components.test.jsx │ ├── containers.jsx │ └── components.jsx │ ├── .eslintrc │ ├── support │ ├── containers.jsx │ ├── components.jsx │ └── __tests__ │ │ └── containers.test.jsx │ ├── swagger-to-html.js │ ├── index.js │ ├── __tests__ │ └── utils.test.js │ ├── utils.js │ └── navigation │ └── __tests__ │ └── components.test.js ├── mockup.png ├── MAINTAINERS ├── layout ├── _partial │ ├── navigation.ejs │ └── google_analytics.ejs ├── page.ejs └── layout.ejs ├── source ├── fonts │ ├── DressCodeIcons.eot │ ├── DressCodeIcons.ttf │ └── DressCodeIcons.woff └── style │ ├── _doc │ ├── mixins.scss │ ├── typography.scss │ ├── support.scss │ ├── index.scss │ ├── content.scss │ ├── search.scss │ ├── layout.scss │ ├── vars.scss │ └── swagger-to-html.scss │ └── _swagger │ ├── swagger-ui-v3.scss │ └── swagger-ui-v2.scss ├── jest.setup.js ├── plugins ├── support.js ├── favicon.js ├── project-partial.js ├── swagger-routes.js ├── swagger-ui.js ├── react-initial-state.js ├── search.js ├── react.js └── swagger-to-html.js ├── ISSUE_TEMPLATE.md ├── .npmignore ├── .editorconfig ├── scripts └── all.js ├── .zappr.yaml ├── jest.json ├── webpack.config.js ├── SECURITY.md ├── zappr.md ├── .eslintrc ├── banner.js ├── .travis.yml ├── CODE_OF_CONDUCT.md └── package.json /index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules/** 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | target 3 | -------------------------------------------------------------------------------- /_doc.scss: -------------------------------------------------------------------------------- 1 | @import "source/style/_doc/index" 2 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/__tests__/templates/body.ejs: -------------------------------------------------------------------------------- 1 | <%= text %> 2 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/__tests__/templates/head.ejs: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | -------------------------------------------------------------------------------- /mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando-incubator/hexo-theme-doc/HEAD/mockup.png -------------------------------------------------------------------------------- /lib/nodejs/.eslintrc: -------------------------------------------------------------------------------- 1 | env: 2 | node: true 3 | parserOptions: 4 | ecmaVersion: 2017 5 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/__tests__/templates/includes/description.ejs: -------------------------------------------------------------------------------- 1 | <%= description %> 2 | -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | Ruben Barilani 2 | Bhaskar Melkani 3 | -------------------------------------------------------------------------------- /layout/_partial/navigation.ejs: -------------------------------------------------------------------------------- 1 |
<%- react_component('Navigation', initial_state) %>
2 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/__tests__/templates/head-include-module.ejs: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | 3 | <%- include_module('subtitle') %> 4 | -------------------------------------------------------------------------------- /source/fonts/DressCodeIcons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando-incubator/hexo-theme-doc/HEAD/source/fonts/DressCodeIcons.eot -------------------------------------------------------------------------------- /source/fonts/DressCodeIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando-incubator/hexo-theme-doc/HEAD/source/fonts/DressCodeIcons.ttf -------------------------------------------------------------------------------- /source/fonts/DressCodeIcons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando-incubator/hexo-theme-doc/HEAD/source/fonts/DressCodeIcons.woff -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/__tests__/templates/head-include-module-doesnt-exist.ejs: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | 3 | <%- include_module('doesnt-exist') %> 4 | -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | const { configure } = require('enzyme'); 2 | const Adapter = require('enzyme-adapter-react-16'); 3 | 4 | configure({ adapter: new Adapter() }); 5 | -------------------------------------------------------------------------------- /lib/browser/polyfills.js: -------------------------------------------------------------------------------- 1 | require('whatwg-fetch'); 2 | const Promise = require('promise-polyfill'); 3 | if (window && !window.Promise) { window.Promise = Promise; } 4 | -------------------------------------------------------------------------------- /lib/nodejs/search/child.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const childOnMessage = require('./child-on-message'); 4 | 5 | process.on('message', childOnMessage({process})); 6 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/__tests__/templates/includes/subtitle.ejs: -------------------------------------------------------------------------------- 1 | <%= subtitle %> 2 | 3 | <%= include_module('description', { description: 'description' }) %> 4 | -------------------------------------------------------------------------------- /plugins/support.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {filter} = require('../lib/nodejs/support'); 4 | 5 | module.exports = ({hexo}) => { 6 | hexo.extend.filter.register('template_locals', filter); 7 | }; 8 | -------------------------------------------------------------------------------- /source/style/_doc/mixins.scss: -------------------------------------------------------------------------------- 1 | // formatting 2 | // ---------- 3 | @mixin doc-inline-code { 4 | background: $doc-color-lighter; 5 | font-size: 90%; 6 | padding: 1px 5px; 7 | border-radius: 2px; 8 | } 9 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const docExclude = require('./docExclude'); 4 | const host = require('./host'); 5 | 6 | module.exports = { 7 | docExclude, 8 | host 9 | }; 10 | -------------------------------------------------------------------------------- /lib/browser/search/actions.js: -------------------------------------------------------------------------------- 1 | const SHOW_SEARCH_RESULTS = 'SHOW_SEARCH_RESULTS'; 2 | const HIDE_SEARCH_RESULTS = 'HIDE_SEARCH_RESULTS'; 3 | 4 | module.exports = { 5 | SHOW_SEARCH_RESULTS, 6 | HIDE_SEARCH_RESULTS 7 | }; 8 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const Swagger = require('./swagger'); 5 | const decorators = require('./decorators'); 6 | 7 | 8 | module.exports = { 9 | Swagger, 10 | decorators 11 | }; 12 | -------------------------------------------------------------------------------- /plugins/favicon.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('../lib/nodejs/hexo-util'); 4 | 5 | module.exports = ({hexo}) => { 6 | const {themeConfig} = util({hexo}); 7 | 8 | themeConfig({ favicon: '/favicon.ico' }); 9 | }; 10 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {parseBody} = require('./lib/parseBody.js'); 4 | const {parseResponse} = require('./lib/parseResponse.js'); 5 | 6 | module.exports = { 7 | parseBody, 8 | parseResponse 9 | }; 10 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/lib/init.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const {traverse} = require('./traverse'); 5 | 6 | function init ({object}){ 7 | return traverse({object, key: '', init: true}); 8 | } 9 | 10 | module.exports = { 11 | init 12 | }; 13 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | 4 | ## Actual Behavior 5 | 6 | 7 | ## Steps to Reproduce the Problem 8 | 9 | 1. 10 | 2. 11 | 3. 12 | 13 | ## Specifications 14 | 15 | - NodeJS Version: 16 | - NPM Version: 17 | - OS: 18 | - Hexo Version: 19 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/operations.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 | <% operations.forEach((operation) => {%> 4 | <%- include_module('operation', { operation, baseUrl, globalSecurity }) %> 5 | <% }) %> 6 |
7 |
8 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/responseSample.ejs: -------------------------------------------------------------------------------- 1 | <% if(sample){ %> 2 |
3 | <%- sample %> 4 | 5 |
6 | <% } %> 7 | -------------------------------------------------------------------------------- /source/style/_doc/typography.scss: -------------------------------------------------------------------------------- 1 | a { @include dc-link } 2 | p { @extend .dc-p } 3 | 4 | h1 { @include dc-h1 } 5 | h2 { @include dc-h2 } 6 | h3 { @include dc-h3 } 7 | h4 { @include dc-h4 } 8 | 9 | hr { 10 | @include dc-divider; 11 | @include dc-divider--secondary; 12 | } 13 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/security.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const security = (ctx) => { 5 | const securityDefinitions = ctx.securityDefinitions; 6 | 7 | return { 8 | securityDefinitions 9 | }; 10 | }; 11 | 12 | 13 | module.exports = security; 14 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/requestSample.ejs: -------------------------------------------------------------------------------- 1 | <% if(curlString){ %> 2 |
3 | <%- curlString %> 4 | 5 |
6 | <% } %> 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # dotfiles 2 | .* 3 | 4 | # tests 5 | *__tests__ 6 | 7 | # tools 8 | banner.js 9 | webpack.config.js 10 | jest* 11 | zappr.* 12 | .travis* 13 | 14 | # misc 15 | mockup* 16 | CONTRIBUTING.md 17 | ISSUE_TEMPLATE.md 18 | CODE_OF_CONDUCT.md 19 | *.log 20 | target 21 | public 22 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/responses.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const responses = (ctx) => { 5 | const operation = ctx.operation; 6 | const responses = operation['responses']; 7 | 8 | return { 9 | responses 10 | }; 11 | }; 12 | 13 | 14 | module.exports = responses; 15 | -------------------------------------------------------------------------------- /source/style/_doc/support.scss: -------------------------------------------------------------------------------- 1 | .doc-support-footer { 2 | margin-top: 3.8rem; 3 | border-top: 2px solid $doc-support-footer-border-color; 4 | padding: 2.4rem 0; 5 | 6 | &__text { 7 | color: $doc-support-footer-color; 8 | } 9 | 10 | &__link { 11 | font-weight: 500; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const { parseResponse, parseBody } = require('./parser'); 5 | const { toSlug , deepMerge, highlight } = require('./utils'); 6 | 7 | module.exports = { 8 | parseResponse, 9 | parseBody, 10 | toSlug, 11 | deepMerge, 12 | highlight 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/lib/processItems/default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {updateResult} = require('../updateResult'); 4 | 5 | const processDefault = ({object, key, isRequired, result}) => { 6 | updateResult({object, key, isRequired, result}); 7 | return result; 8 | }; 9 | 10 | module.exports = processDefault; 11 | -------------------------------------------------------------------------------- /lib/browser/.eslintrc: -------------------------------------------------------------------------------- 1 | plugins: 2 | - react 3 | env: 4 | browser: true 5 | parserOptions: 6 | sourceType: module 7 | ecmaFeatures: 8 | experimentalObjectRestSpread: true 9 | jsx: true 10 | globals: 11 | module: true 12 | $: true 13 | 14 | rules: 15 | # React 16 | react/jsx-uses-react: 1 17 | react/jsx-uses-vars: 1 18 | -------------------------------------------------------------------------------- /layout/page.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | <%- page.content %> 7 | 8 |
9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/lib/parseBody.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {init} = require('./init'); 4 | 5 | const parseBody = (body) => { 6 | 7 | if (!body || !body.schema){ 8 | return {}; 9 | } 10 | 11 | const schema = body.schema; 12 | 13 | return init({ object: schema }); 14 | }; 15 | 16 | module.exports = { 17 | parseBody 18 | }; 19 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('swagger.index', () => { 4 | it('should expose swagger and decorators', () => { 5 | 6 | const swagger = require('../index'); 7 | 8 | expect(Object.keys(swagger).length).toBe(2); 9 | expect(swagger.Swagger).toBeDefined(); 10 | expect(swagger.decorators).toBeDefined(); 11 | }); 12 | 13 | }); 14 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/lib/processItems/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const processEnum = require('./enum'); 4 | const processArray = require('./array'); 5 | const processObject = require('./object'); 6 | const processDefault = require('./default'); 7 | 8 | module.exports = { 9 | processEnum, 10 | processArray, 11 | processObject, 12 | processDefault 13 | }; 14 | -------------------------------------------------------------------------------- /plugins/project-partial.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {createHelper} = require('../lib/nodejs/project-partial'); 4 | 5 | module.exports = ({hexo}) => { 6 | 7 | hexo.extend.helper.register('project_partial', createHelper({ 8 | theme_config: hexo.config.theme_config, 9 | source_dir: hexo.source_dir, 10 | render: hexo.render, 11 | log: hexo.log 12 | })); 13 | 14 | }; 15 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/head.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | <%= info.title %> 5 |

6 |
7 | Version: <%= info.version %> 8 |
9 |
10 |
11 | <%= info.description %> 12 |
13 |
14 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('decocators.index', () => { 4 | 5 | it('should expost decocators', () => { 6 | const decorators = require('../index'); 7 | 8 | expect(Object.keys(decorators).length).toBe(2); 9 | 10 | expect(decorators.docExclude).toBeDefined(); 11 | expect(decorators.host).toBeDefined(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/lib/processItems/enum.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {updateResult} = require('../updateResult'); 4 | 5 | const processEnum = ({object, key, isRequired, result}) => { 6 | object.type = 'enum'; 7 | object.values = object.enum; 8 | updateResult({object, key, isRequired, result}); 9 | return result; 10 | }; 11 | 12 | module.exports = processEnum; 13 | 14 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/requestBody.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const { parseBody } = require('../../helpers'); 5 | 6 | 7 | const requestBody = ( ctx ) => { 8 | const body = []; 9 | 10 | ctx['body'].forEach((item) => { 11 | body.push(parseBody(item)); 12 | }); 13 | 14 | return { 15 | body 16 | }; 17 | }; 18 | 19 | 20 | module.exports = requestBody; 21 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/filters/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const filterOperations = require('./operations'); 4 | const filterParameters = require('./parameters'); 5 | const filterPaths = require('./paths'); 6 | const filterSecurity = require('./security'); 7 | 8 | 9 | module.exports = { 10 | filterOperations, 11 | filterParameters, 12 | filterPaths, 13 | filterSecurity 14 | }; 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # /.editorconfig 2 | # EditorConfig helps developers define and maintain consistent 3 | # coding styles between different editors and IDEs 4 | # editorconfig.org 5 | 6 | root = true 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | # We recommend you to keep these unchanged 13 | end_of_line = lf 14 | charset = utf-8 15 | trim_trailing_whitespace = true 16 | insert_final_newline = true 17 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-ui/parse-schema-file-error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function ParseSchemaFileError ({message, filePath, referencePath}) { 4 | Error.captureStackTrace(this, this.constructor); 5 | this.name = this.constructor.name; 6 | this.message = message; 7 | this.filePath = filePath; 8 | this.referencePath = referencePath; 9 | }; 10 | 11 | require('util').inherits(module.exports, Error); 12 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/operation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const { deepMerge } = require('../../helpers'); 5 | 6 | const operation = (ctx) => { 7 | if (ctx.operation.security){ 8 | deepMerge(ctx.operation.security, ctx.globalSecurity); 9 | } else { 10 | ctx.operation.security = ctx.globalSecurity; 11 | } 12 | 13 | return ctx; 14 | }; 15 | 16 | 17 | module.exports = operation; 18 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/requestContent.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 | Request Body 4 |
5 | <% if(request.formData.length){ %> 6 | <%- include_module('formData', { params: request.formData }) %> 7 | <% } %> 8 | <% if(request.body.length){ %> 9 | <%- include_module('requestBody', { body: request.body }) %> 10 | <% } %> 11 |
12 | 13 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('parser.index', () => { 4 | const parser = require('../index'); 5 | 6 | test('should export getBody, getResponse and filterResponses', () => { 7 | const {parseBody, parseResponse} = parser; 8 | expect(Object.keys(parser).length).toBe(2); 9 | expect(parseBody).toBeInstanceOf(Function); 10 | expect(parseResponse).toBeInstanceOf(Function); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /lib/browser/support/containers.jsx: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const SupportFooterComponent = require('./components.jsx').SupportFooter; 3 | 4 | class SupportFooter extends React.Component { 5 | constructor (props) { 6 | super(props); 7 | } 8 | 9 | render () { 10 | if (!this.props.page.support) { return null; } 11 | 12 | return (); 13 | } 14 | } 15 | 16 | module.exports = {SupportFooter}; 17 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/lib/processItems/array.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {traverse} = require('../traverse'); 4 | const {updateResult} = require('../updateResult'); 5 | 6 | const processArray = ({object, key, isRequired, result}) => { 7 | updateResult({object, key, isRequired, result}); 8 | const items = object.items; 9 | return traverse({ 10 | object: items, 11 | key: key + '[]' 12 | }); 13 | }; 14 | 15 | module.exports = processArray; 16 | -------------------------------------------------------------------------------- /lib/browser/support/components.jsx: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | function SupportFooter ({support}) { 4 | return ( 5 |
6 | 7 |  {support.link_text} 8 |
9 | ); 10 | } 11 | 12 | 13 | module.exports = {SupportFooter}; 14 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/lib/parseResponse.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {init} = require('./init'); 4 | 5 | const parseResponse = (response) => { 6 | 7 | if (!response){ 8 | return {}; 9 | } 10 | 11 | const schema = response.schema; 12 | 13 | if (schema){ 14 | return init({ object: schema }); 15 | } else { 16 | response.__noData = true; 17 | return response; 18 | } 19 | }; 20 | 21 | module.exports = { 22 | parseResponse 23 | }; 24 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { filterSecurity, filterParameters, filterPaths, filterOperations} = require('./filters'); 4 | 5 | const docExclude = (swagger) => { 6 | 7 | swagger = filterSecurity(swagger); 8 | 9 | swagger = filterParameters(swagger); 10 | 11 | swagger = filterPaths(swagger); 12 | 13 | swagger = filterOperations(swagger); 14 | 15 | return swagger; 16 | }; 17 | 18 | 19 | module.exports = docExclude; 20 | -------------------------------------------------------------------------------- /scripts/all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global hexo */ 4 | 5 | require('../plugins/favicon')({hexo}); 6 | require('../plugins/project-partial')({hexo}); 7 | require('../plugins/search')({hexo}); 8 | require('../plugins/swagger-to-html')({hexo}); 9 | require('../plugins/swagger-ui')({hexo}); 10 | require('../plugins/react')({hexo}); 11 | require('../plugins/react-initial-state')({hexo}); 12 | require('../plugins/support')({hexo}); 13 | require('../plugins/swagger-routes')({hexo}); 14 | -------------------------------------------------------------------------------- /source/style/_doc/index.scss: -------------------------------------------------------------------------------- 1 | @import "dress-code/dist/sass/dress-code"; 2 | 3 | @import "./vars"; 4 | @import "./mixins"; 5 | 6 | // include dc-* selectors after variables to allow customization 7 | @include dc-everything; 8 | 9 | // core 10 | @import "./typography"; 11 | @import "./layout"; 12 | @import "./navigation"; 13 | @import "./content"; 14 | @import "./formatting"; 15 | 16 | // components 17 | @import "./search"; 18 | @import "./swagger-to-html"; 19 | @import "./support"; 20 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/__tests__/responses.test.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | const responsesController = require('../responses'); 5 | 6 | describe('controllers.resposnes', () => { 7 | it('should transform context as expected', () => { 8 | const ctx = { 9 | operation: { 10 | responses: 'foo' 11 | } 12 | }; 13 | 14 | const { responses } = responsesController(ctx); 15 | 16 | expect(responses).toBe('foo'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/filters/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { isExclude } = require('../utils'); 4 | 5 | 6 | const filterPaths = (swagger) => { 7 | 8 | swagger.paths && Object 9 | .keys(swagger.paths) 10 | .forEach((key) => { 11 | const path = swagger.paths[key]; 12 | if (isExclude(path)){ 13 | delete swagger.paths[key]; 14 | } 15 | }); 16 | 17 | return swagger; 18 | }; 19 | 20 | 21 | module.exports = filterPaths; 22 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/security.ejs: -------------------------------------------------------------------------------- 1 | <% if(securityDefinitions){ %> 2 |
3 |

Security Definitions

4 |
5 | <% Object.keys(securityDefinitions).forEach((key) => { 6 | const definition = securityDefinitions[key]; 7 | %> 8 | <%- include_module('securityDefinition', { key, definition }) %> 9 | <% }) %> 10 |
11 |
12 | <% } %> 13 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const modules = require('./modules'); 4 | const {createTransformer} = require('./core'); 5 | 6 | module.exports = ({hexo}) => createTransformer({ 7 | input: (api) => { 8 | const swaggerStore = require('../swagger-store')({hexo}); 9 | return swaggerStore 10 | .getSwagger(api, true) 11 | .then(({swagger}) => { 12 | return Promise.resolve(swagger.swaggerObject); 13 | }); 14 | }, 15 | modules 16 | }); 17 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/__tests__/lib/processItems/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('swagger-parser.lib.processItems.index', () => { 4 | test('should export processors', () => { 5 | 6 | const index = require('../../../lib/processItems/index'); 7 | 8 | expect(index.processEnum).toBeDefined(); 9 | expect(index.processArray).toBeDefined(); 10 | expect(index.processObject).toBeDefined(); 11 | expect(index.processDefault).toBeDefined(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /lib/nodejs/search/child-on-message.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const build = require('./build'); 4 | const createLogger = require('hexo-log'); 5 | 6 | module.exports = ({process}) => { 7 | 8 | return function (message) { 9 | const pages = message.pages ? Array.from(message.pages.data) : []; 10 | const logger = createLogger({ debug: message.debug }); 11 | const rootPath = message.rootPath || ''; 12 | const result = build({pages, logger, rootPath}); 13 | process.send(result); 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/filters/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('docExclude.index', () => { 4 | it('should expose filters', () => { 5 | const filters = require('../index'); 6 | 7 | expect(Object.keys(filters).length).toBe(4); 8 | expect(filters.filterOperations).toBeDefined(); 9 | expect(filters.filterParameters).toBeDefined(); 10 | expect(filters.filterPaths).toBeDefined(); 11 | expect(filters.filterSecurity).toBeDefined(); 12 | }); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/host.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const docKey = 'x-doc'; 4 | const hostKey = 'host'; 5 | 6 | const host = (swagger) => { 7 | 8 | let host; 9 | 10 | if (swagger && swagger[docKey] && swagger[docKey][hostKey]){ 11 | host = swagger[docKey][hostKey]; 12 | delete swagger[docKey][hostKey]; 13 | } 14 | // Add more ways to provide host and order them in priority. 15 | 16 | if (host){ 17 | swagger.host = host; 18 | } 19 | 20 | return swagger; 21 | }; 22 | 23 | 24 | module.exports = host; 25 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/securityRequirement.ejs: -------------------------------------------------------------------------------- 1 | <% securityRequirement.forEach((security) => { 2 | Object.keys(security).forEach((key) => { 3 | %> 4 |
5 | <% if(key && security[key].length){ %> 6 | <%- security[key].join(', ').replace(/,([^,]*)$/, ' and$1') %> <% if(security[key].length > 1){ %>scopes are<%}else{ %>scope is<% } %> required for <%- key %>. 7 | <% } %> 8 |
9 | <% })}) %> 10 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/response.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const { parseResponse} = require('../../helpers'); 5 | 6 | 7 | const response = (ctx) => { 8 | const responseCode = ctx['responseCode']; 9 | const originalResponse = ctx['response']; 10 | const responseData = parseResponse(originalResponse); 11 | const response = { 12 | description: originalResponse.description, 13 | data: responseData 14 | }; 15 | 16 | return { 17 | responseCode, 18 | response 19 | }; 20 | }; 21 | 22 | 23 | module.exports = response; 24 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/responseSample.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { highlight } = require('../../helpers'); 3 | 4 | 5 | const responseSample = (ctx) => { 6 | let sample = ctx.sample; 7 | if (sample && sample['application/json']){ 8 | sample = sample['application/json']; 9 | } 10 | if (sample){ 11 | const code = JSON.stringify(sample, null, 2); 12 | sample = highlight({ 13 | code, 14 | lang: 'json' 15 | }); 16 | } 17 | 18 | return { 19 | sample 20 | }; 21 | }; 22 | 23 | 24 | module.exports = responseSample; 25 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/__tests__/lib/init.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockTraverse = jest.fn(); 4 | 5 | jest.mock('../../lib/traverse', () => ({ 6 | traverse: mockTraverse 7 | })); 8 | 9 | const {init} = require('../../lib/init'); 10 | 11 | describe('swagger-parser.lib.init', () => { 12 | test('should init traverse', () => { 13 | const object = {foo: 'bar'}; 14 | 15 | init({object}); 16 | 17 | expect(mockTraverse).toHaveBeenCalled(); 18 | expect(mockTraverse).toHaveBeenCalledWith({object, key: '', init: true}); 19 | }); 20 | 21 | }); 22 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/__tests__/security.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const securityController = require('../security'); 4 | 5 | describe('security.controller', () => { 6 | test('should return the context as expected', () => { 7 | const ctx = { 8 | securityDefinitions: 'securityDefinitions' 9 | }; 10 | 11 | const updatedCtx = securityController(ctx); 12 | const {securityDefinitions} = updatedCtx; 13 | 14 | expect(Object.keys(updatedCtx).length).toBe(1); 15 | expect(securityDefinitions).toBe('securityDefinitions'); 16 | 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/__tests__/host.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | describe('decorators.host', () => { 5 | it('should update host', () => { 6 | const hostDecorator = require('../host'); 7 | 8 | const swagger = { 9 | host: 'ORIGINAL_HOST', 10 | 'x-doc': { 11 | host: 'UPDATED_HOST' 12 | } 13 | }; 14 | 15 | const updatedSwagger = hostDecorator(swagger); 16 | 17 | const expectedSwagger = { 18 | host: 'UPDATED_HOST', 19 | 'x-doc': {} 20 | }; 21 | 22 | expect(updatedSwagger).toEqual(expectedSwagger); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /.zappr.yaml: -------------------------------------------------------------------------------- 1 | approvals: 2 | minimum: 2 # PR needs at least 2 approval for compliant reasons 3 | pattern: "^(:\\+1:|👍|approved)$" # write a comment to the PR with "approved" or ":+1" 4 | veto: 5 | pattern: "^(:\\-1:|👎|rejected)$" # write a comment to the PR with "rejected" or ":-1" 6 | from: 7 | collaborators: true 8 | commit: 9 | message: 10 | patterns: 11 | # Follow MC commit conventions. 12 | # CONTRIBUTING.md#-git-commit-guidelines 13 | - "^(feat|fix|docs|style|refactor|perf|test|chore)(|\\([a-zA-Z0-9-._]+\\)):.{3,}" 14 | 15 | X-Zalando-Type: code 16 | -------------------------------------------------------------------------------- /jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": true, 3 | "clearMocks": true, 4 | "testRegex": "(/__tests__/.*\\.(test|spec))\\.(js|jsx)$", 5 | "setupFiles": [ 6 | "raf/polyfill", 7 | "./jest.setup.js" 8 | ], 9 | "collectCoverageFrom": [ 10 | "!**/__tests__/*.{js,jsx}", 11 | "lib/browser/**/*.{js,jsx}", 12 | "lib/nodejs/**/*.js", 13 | "!lib/browser/sidebar/index.js" 14 | ], 15 | "testResultsProcessor": "./node_modules/jest-junit", 16 | "coverageDirectory": "target/coverage", 17 | "coverageReporters": [ 18 | "json", 19 | "lcov", 20 | "html", 21 | "cobertura" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | externals: { 6 | jquery: '$', 7 | lunr: true, 8 | }, 9 | entry: { 10 | 'doc': './lib/browser/index.js', 11 | }, 12 | output: { 13 | path: path.resolve(__dirname, 'source/script'), 14 | filename: '[name].js' 15 | }, 16 | module: { 17 | loaders: [ 18 | { 19 | test: /.jsx?$/, 20 | loader: 'babel-loader', 21 | exclude: /node_modules/, 22 | query: { 23 | presets: ['es2015', 'react'] 24 | } 25 | } 26 | ] 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | We acknowledge that every line of code that we write may potentially contain security issues. 2 | We are trying to deal with it responsibly and provide patches as quickly as possible. 3 | 4 | We host our bug bounty program on HackerOne, it is currently private, therefore if you would like to report a vulnerability and get rewarded for it, please ask to join our program by filling this form: 5 | 6 | https://corporate.zalando.com/en/services-and-contact#security-form 7 | 8 | You can also send your report via this form if you do not want to join our bug bounty program and just want to report a vulnerability or security issue. 9 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/filters/operations.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { isExclude } = require('../utils'); 4 | 5 | 6 | const filterOperations = (swagger) => { 7 | 8 | swagger.paths && Object 9 | .keys(swagger.paths) 10 | .forEach((key) => { 11 | const path = swagger.paths[key] ; 12 | path && Object 13 | .keys(path) 14 | .forEach((verb) => { 15 | const operation = path[verb]; 16 | if (isExclude(operation)){ 17 | delete path[verb]; 18 | } 19 | }); 20 | }); 21 | 22 | return swagger; 23 | }; 24 | 25 | 26 | module.exports = filterOperations; 27 | -------------------------------------------------------------------------------- /layout/_partial/google_analytics.ejs: -------------------------------------------------------------------------------- 1 | <% if (config.theme_config.google_analytics){ %> 2 | 12 | <% } %> 13 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-ui/partials/v2/libs.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /plugins/swagger-routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getFilter = (hexo) => { 4 | 5 | const swaggerStore = require('../lib/nodejs/swagger-store')({hexo}); 6 | 7 | return () => { 8 | const routes = swaggerStore.getRoutes(); 9 | routes && Object 10 | .keys(routes) 11 | .forEach((route) => { 12 | const data = routes[route]; 13 | 14 | if(data){ 15 | hexo.route.set(route, data); 16 | }else{ 17 | hexo.route.remove(route); 18 | } 19 | }) 20 | } 21 | } 22 | 23 | 24 | module.exports = ({hexo}) => { 25 | const filter = getFilter(hexo); 26 | hexo.extend.filter.register('after_generate', filter); 27 | }; 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const operation = require('./operation'); 4 | const operations = require('./operations'); 5 | const request = require('./request'); 6 | const requestBody = require('./requestBody'); 7 | const requestSample = require('./requestSample'); 8 | const response = require('./response'); 9 | const responses = require('./responses'); 10 | const responseSample = require('./responseSample'); 11 | const security = require('./security'); 12 | 13 | 14 | module.exports = { 15 | operation, 16 | operations, 17 | request, 18 | requestBody, 19 | requestSample, 20 | response, 21 | responses, 22 | responseSample, 23 | security 24 | }; 25 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Key for exclude flag. 5 | */ 6 | const docKey = 'x-doc'; 7 | const excludeKey = 'excluded'; 8 | 9 | /** 10 | * Function to decide what objects to exclude from the schema. 11 | */ 12 | const isExclude = (subject) => { 13 | if (subject.hasOwnProperty(docKey)){ 14 | const isExcludable = !!(subject[docKey] && subject[docKey][excludeKey]); 15 | // Delete the key as its not a standard swagger key and will cause issues while rendering in SwaggerUI or Swagger-to-html 16 | delete subject[docKey]; 17 | return isExcludable; 18 | } 19 | return false; 20 | }; 21 | 22 | module.exports = { 23 | isExclude 24 | }; 25 | -------------------------------------------------------------------------------- /lib/browser/search/load.js: -------------------------------------------------------------------------------- 1 | const lunr = require('lunr'); 2 | const searcher = require('../../nodejs/search/searcher'); 3 | 4 | module.exports = function load (url) { 5 | return fetchIndex(url) 6 | .then((data) => { 7 | return searcher({ 8 | index: data.index, 9 | store: data.store 10 | }); 11 | }); 12 | }; 13 | 14 | function fetchIndex (url) { 15 | return fetch(url || '/lunr.json', { credentials: 'include' }) 16 | .then(function (res) { 17 | return res.json(); 18 | }) 19 | .then(function (json) { 20 | const index = lunr.Index.load(json.index); 21 | const store = json.store; 22 | return { index: index, store: store }; 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const index = require('../index'); 4 | 5 | describe('controllers.index', () => { 6 | it('should return controllers', () => { 7 | expect(Object.keys(index).length).toBe(9); 8 | expect(index.operation).toBeDefined(); 9 | expect(index.operations).toBeDefined(); 10 | expect(index.request).toBeDefined(); 11 | expect(index.requestBody).toBeDefined(); 12 | expect(index.requestSample).toBeDefined(); 13 | expect(index.response).toBeDefined(); 14 | expect(index.responses).toBeDefined(); 15 | expect(index.responseSample).toBeDefined(); 16 | expect(index.security).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/filters/__tests__/paths.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const pathsFilter = require('../paths'); 4 | 5 | const dummySwagger = { 6 | paths: { 7 | '/pets': { 8 | 'get': { 9 | } 10 | }, 11 | '/pets/id':{ 12 | 'x-doc': { 13 | 'excluded': true 14 | } 15 | } 16 | } 17 | }; 18 | 19 | const expectedSwagger = { 20 | paths: { 21 | '/pets': { 22 | 'get': { 23 | } 24 | } 25 | } 26 | }; 27 | 28 | describe('docExclude.paths', () => { 29 | it('should filter paths', () => { 30 | const updatedSwagger = pathsFilter(dummySwagger); 31 | 32 | expect(updatedSwagger).toEqual(expectedSwagger); 33 | }); 34 | }); 35 | 36 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-ui/partials/v3/snippet.ejs: -------------------------------------------------------------------------------- 1 |
2 | 22 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/request.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const request = (ctx) => { 5 | const operation = ctx.operation; 6 | const parameters = operation.parameters; 7 | const baseUrl = ctx.baseUrl; 8 | const path = operation.path; 9 | const verb = operation.verb; 10 | const request = { 11 | header: [], 12 | path: [], 13 | query: [], 14 | formData: [], 15 | body: [] 16 | }; 17 | 18 | parameters && parameters.forEach((param) => { 19 | const paramType = param['in']; 20 | request[paramType] && request[paramType].push(param); 21 | }); 22 | 23 | return { 24 | request, 25 | path, 26 | verb, 27 | baseUrl 28 | }; 29 | }; 30 | 31 | 32 | module.exports = request; 33 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/filters/__tests__/operations.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const operationsFilter = require('../operations'); 4 | 5 | const dummySwagger = { 6 | paths: { 7 | '/pets': { 8 | 'get': { 9 | 'x-doc':{ 10 | 'excluded': true 11 | } 12 | }, 13 | 'post':{ 14 | } 15 | } 16 | } 17 | }; 18 | 19 | const expectedSwagger = { 20 | paths: { 21 | '/pets': { 22 | 'post':{ 23 | } 24 | } 25 | } 26 | }; 27 | 28 | describe('docExclude.operations', () => { 29 | it('should filter operations', () => { 30 | const updatedSwagger = operationsFilter(dummySwagger); 31 | 32 | expect(updatedSwagger).toEqual(expectedSwagger); 33 | }); 34 | }); 35 | 36 | -------------------------------------------------------------------------------- /source/style/_doc/content.scss: -------------------------------------------------------------------------------- 1 | // 2 | // content 3 | // 4 | .doc-content { 5 | position: relative; 6 | top: 0; 7 | right: 0; 8 | bottom: 0; 9 | left: 0; 10 | width: auto; 11 | height: auto; 12 | min-height: 100vh; 13 | padding-top: $doc-navbar-height; 14 | padding-bottom: 420px; 15 | z-index: 2; 16 | transition: 0.1s ease-in-out; 17 | 18 | transform: translateX($doc-sidebar-width); 19 | margin-right: $doc-sidebar-width; 20 | 21 | .dc-page { 22 | overflow: auto; 23 | padding-top: 0; 24 | 25 | @media screen and (max-width: $doc-breakpoint) { 26 | padding-top: 0.8rem; 27 | } 28 | } 29 | 30 | @media screen and (max-width: $doc-breakpoint) { 31 | transform: translateX(0); 32 | margin-right: 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/nodejs/search/__tests__/mocks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockLogger = { 4 | info: () => {}, 5 | error: () => {}, 6 | debug: () => {} 7 | }; 8 | 9 | const mockHexo = { 10 | env: {}, 11 | config: { 12 | theme_config: { 13 | search: { 14 | route: 'testlunr.json' 15 | } 16 | } 17 | }, 18 | route: { 19 | 'set': () => {} 20 | }, 21 | log: mockLogger 22 | }; 23 | 24 | 25 | const mockCb = () => {}; 26 | 27 | const mockHexoUtil = () => { 28 | return { 29 | url_for: (path) => { 30 | return path; 31 | }, 32 | themeConfig: () => { 33 | return mockHexo.theme_config; 34 | } 35 | }; 36 | }; 37 | 38 | module.exports = { 39 | mockHexo, 40 | mockLogger, 41 | mockCb, 42 | mockHexoUtil 43 | }; 44 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/__tests__/requestBody.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockParseBody = jest.fn(() => 'flatBody'); 4 | const mockHelpers = { 5 | parseBody: mockParseBody 6 | }; 7 | 8 | jest.mock('../../../helpers', () => mockHelpers); 9 | 10 | 11 | const requestBodyController = require('../requestBody'); 12 | 13 | describe('controllers.requestBody', () => { 14 | it('should transform the context as expected', () => { 15 | const ctx = { 16 | request: {}, 17 | body: [ 18 | {}, 19 | {} 20 | ] 21 | }; 22 | const {body} = requestBodyController(ctx); 23 | expect(body).toBeInstanceOf(Array); 24 | expect(body.length).toBe(2); 25 | expect(body[0]).toBe('flatBody'); 26 | }); 27 | }); 28 | 29 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/__tests__/lib/processItems/default.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockUpdateResult = jest.fn(); 4 | 5 | jest.mock('../../../lib/updateResult', () => ({ 6 | updateResult: mockUpdateResult 7 | })); 8 | 9 | const processDefault = require('../../../lib/processItems/default'); 10 | 11 | describe('swagger-parser.lib.processItems.default', () => { 12 | test('should process default values', () => { 13 | const object = {}; 14 | const key = 'key'; 15 | const isRequired = true; 16 | const result = {}; 17 | 18 | processDefault({object, key, isRequired, result}); 19 | 20 | expect(mockUpdateResult).toHaveBeenCalled(); 21 | expect(mockUpdateResult).toHaveBeenCalledWith({object, key, isRequired, result}); 22 | 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /lib/browser/swagger-to-html.js: -------------------------------------------------------------------------------- 1 | const Clipboard = require('clipboard'); 2 | 3 | const clipboard = new Clipboard('.doc-swagger-to-html .sample-snippet__copy-btn', { 4 | text: function (triggerElem) { 5 | const preBlock = triggerElem.parentNode.querySelector('pre') ; 6 | const textToCopy = preBlock.textContent; 7 | return textToCopy; 8 | } 9 | }); 10 | 11 | // Show the tooltip; 12 | clipboard.on('success', function (event){ 13 | const trigger = event.trigger; 14 | trigger.classList.add('dc--has-tooltip'); 15 | trigger.classList.add('dc--has-tooltip--bottom'); 16 | 17 | trigger.addEventListener('mouseleave', (event) => { 18 | event.stopPropagation(); 19 | trigger.classList.remove('dc--has-tooltip'); 20 | trigger.classList.remove('dc--has-tooltip--bottom'); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-ui/parse-schema-file.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const ParseSchemaFileError = require('./parse-schema-file-error.js'); 4 | 5 | function parseSchemaFile (filePath, pageSource, hexo) { 6 | 7 | const swaggerStore = require('../swagger-store')({hexo}); 8 | try { 9 | return swaggerStore 10 | .getSwagger(filePath) 11 | .then(({swagger, downloadRoute}) => { 12 | return { 13 | downloadRoute, 14 | pageSource, 15 | swagger: swagger.swaggerJson 16 | }; 17 | }); 18 | } catch (error){ 19 | return Promise.reject(new ParseSchemaFileError({ 20 | 'message': 'There is an error reading the file.', 21 | filePath, 22 | 'referencePath': pageSource 23 | })); 24 | } 25 | } 26 | 27 | module.exports = parseSchemaFile; 28 | -------------------------------------------------------------------------------- /plugins/swagger-ui.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const createSwaggerUI = require('../lib/nodejs/swagger-ui/index'); 4 | const util = require('../lib/nodejs/hexo-util'); 5 | 6 | module.exports = ({hexo}) => { 7 | const {themeConfig} = util({hexo}); 8 | const {swaggerUITag, swaggerUIProcessor} = createSwaggerUI({hexo}); 9 | 10 | themeConfig({ swagger_ui: createSwaggerUI.DEFAULT_CONFIG }); 11 | 12 | hexo.extend.tag.register('swagger_ui', swaggerUITag, {async: true}); 13 | hexo.extend.tag.register('swagger_ui_advanced', swaggerUITag, {async: true, ends: true}); 14 | 15 | /** 16 | * This funtion is called when any file is processed. 17 | * It is automatically hooked to the watch task and is called if any file is modified. 18 | * */ 19 | hexo.extend.processor.register('*', swaggerUIProcessor); 20 | }; 21 | -------------------------------------------------------------------------------- /lib/nodejs/support/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const DEFAULT_CONFIG = { 4 | link_url: '', 5 | link_text: 'Contact Us', 6 | text: 'Didn\'t you find what are you looking for?
Try searching again on the left menu or', 7 | navigation: true, 8 | navigation_label: 'SUPPORT' 9 | }; 10 | 11 | const filter = (locals) => { 12 | if (locals.page.support === false) { 13 | return locals; 14 | } 15 | if (locals.config.theme_config.support) { 16 | locals.config.theme_config.support = Object.assign({}, DEFAULT_CONFIG, locals.config.theme_config.support); 17 | locals.page.support = Object.assign( 18 | {}, 19 | locals.config.theme_config.support, 20 | locals.page.support || {} 21 | ); 22 | } 23 | return locals; 24 | }; 25 | 26 | module.exports = {DEFAULT_CONFIG, filter}; 27 | -------------------------------------------------------------------------------- /zappr.md: -------------------------------------------------------------------------------- 1 | # Zappr 2 | 3 | We use [zappr](https://github.com/zalando/zappr) to enforce commit message patterns, PR specification correctness, etc. 4 | 5 | 6 | ## Zappr Template for Code Repositories 7 | 8 | ```yaml 9 | approvals: 10 | minimum: 2 # PR needs at least 2 approvals 11 | pattern: "^(:\\+1:|👍|approved)$" # write a comment to the PR with "approved" or ":+1" 12 | veto: 13 | pattern: "^(:\\-1:|👎|rejected)$" # write a comment to the PR with "rejected" or ":-1" 14 | from: 15 | orgs: 16 | - zalando-incubator 17 | - zalando 18 | collaborators: true 19 | commit: 20 | message: 21 | patterns: 22 | # follow commit guidelines CONTRIBUTING.md#git-commit-guidelines 23 | - "^(feat|fix|docs|style|refactor|perf|test|chore)(|\\([a-zA-Z0-9-._]+\\)):.{3,}" 24 | ``` 25 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | extends: 2 | - eslint:recommended 3 | plugins: 4 | - jest 5 | env: 6 | es6: true 7 | jest: true 8 | node: true 9 | rules: 10 | semi: 2 11 | strict: 2 12 | indent: 13 | - error 14 | - 2 15 | quotes: 16 | - 1 17 | - single 18 | - avoid-escape 19 | space-before-function-paren: 20 | - 2 21 | - always 22 | keyword-spacing: 23 | - 2 24 | - before: true 25 | after: true 26 | space-infix-ops: 2 27 | spaced-comment: 28 | - 2 29 | - always 30 | arrow-spacing: 2 31 | no-console: 2 32 | no-empty: 2 33 | eqeqeq: 34 | - 2 35 | - always 36 | no-unused-vars: 2 37 | no-unsafe-negation: 2 38 | prefer-const: 2 39 | no-var: 2 40 | 41 | # Jest 42 | jest/no-disabled-tests: 1 43 | jest/no-focused-tests: 1 44 | jest/no-identical-title: 2 45 | -------------------------------------------------------------------------------- /source/style/_swagger/swagger-ui-v3.scss: -------------------------------------------------------------------------------- 1 | .hexo-swagger-ui-v3 { 2 | // "namespace" required vendors 3 | @import "./swagger-ui-v3-vendors.scss"; 4 | 5 | .swagger-ui { 6 | 7 | .wrapper { 8 | padding: 0; 9 | } 10 | 11 | .information-container .info { 12 | margin: 0; 13 | } 14 | 15 | .info .title { 16 | font-size: 24px; 17 | font-weight: bold; 18 | } 19 | 20 | .info a { 21 | font-weight: 600; 22 | } 23 | 24 | .info .title small { 25 | display: none; 26 | } 27 | 28 | .info .base-url { 29 | padding: 0; 30 | background: transparent; 31 | } 32 | 33 | .scheme-container { 34 | padding-right: 12px; 35 | padding-left: 12px; 36 | } 37 | 38 | .markdown p { 39 | margin-bottom: 0; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/browser/index.js: -------------------------------------------------------------------------------- 1 | require('./polyfills'); 2 | 3 | const React = require('react'); 4 | const ReactDOM = require('react-dom'); 5 | const {Navigation} = require('./navigation/containers.jsx'); 6 | const {SearchResults} = require('./search/containers.jsx'); 7 | const {SupportFooter} = require('./support/containers.jsx'); 8 | const PROPS = Object.assign({}, window.__INITIAL_STATE__, {log: console}); 9 | 10 | require('./swagger-to-html'); 11 | 12 | ReactDOM.hydrate( 13 | React.createFactory(Navigation)(PROPS), 14 | document.getElementById('react-navigation-root') 15 | ); 16 | 17 | ReactDOM.render( 18 | React.createFactory(SearchResults)(PROPS), 19 | document.getElementById('react-search-results-root') 20 | ); 21 | 22 | ReactDOM.render( 23 | React.createFactory(SupportFooter)(PROPS), 24 | document.getElementById('react-support-footer-root') 25 | ); 26 | -------------------------------------------------------------------------------- /lib/browser/support/__tests__/containers.test.jsx: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const {shallow} = require('enzyme'); 3 | 4 | describe('browser.support.conatiners', () => { 5 | describe('SupportFooter', () => { 6 | const {SupportFooter} = require('../containers.jsx'); 7 | 8 | it('should not render when page.support is falsy', () => { 9 | const page = { 10 | support: false 11 | }; 12 | const supportFooter = shallow(); 13 | expect(supportFooter.getElement()).toBe(null); 14 | }); 15 | 16 | it('should render when page.support is not falsy', () => { 17 | const page = { 18 | support: true 19 | }; 20 | const supportFooter = shallow(); 21 | expect(supportFooter.getElement()).not.toBe(null); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/responses.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Response

5 |
6 |
7 |

Sample Response

8 |
9 |
10 | <% Object.keys(responses).forEach((responseCode) => { 11 | const response = responses[responseCode]; 12 | %> 13 | 14 | 15 | 18 | 21 | 22 |
16 | <%- include_module('response', { responseCode, response }) %> 17 | 19 | <%- include_module('responseSample', { sample: response['examples']}) %> 20 |
23 | <% }) %> 24 |
25 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/__tests__/lib/processItems/enum.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockUpdateResult = jest.fn(); 4 | 5 | jest.mock('../../../lib/updateResult', () => ({ 6 | updateResult: mockUpdateResult 7 | })); 8 | 9 | const processEnum = require('../../../lib/processItems/enum'); 10 | 11 | describe('swagger-parser.lib.processItems.enum', () => { 12 | test('should process enum', () => { 13 | const object = { 14 | enum: [1, 2] 15 | }; 16 | const key = 'key'; 17 | const isRequired = true; 18 | const result = {}; 19 | 20 | processEnum({object, key, isRequired, result}); 21 | 22 | object.type = 'enum'; 23 | object.values = object.enum; 24 | 25 | expect(mockUpdateResult).toHaveBeenCalled(); 26 | expect(mockUpdateResult).toHaveBeenCalledWith({object, key, isRequired, result}); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('swagger-to-html', () => { 4 | 5 | const mockGetSwagger = jest.fn() 6 | .mockImplementationOnce(() => { 7 | return Promise.resolve({ 8 | swagger: { 9 | swaggerObject: 'SWAGGER_OBJECT' 10 | } 11 | }); 12 | }); 13 | 14 | jest.mock('../../swagger-store', () => { 15 | return () => ({ 16 | getSwagger: mockGetSwagger 17 | }); 18 | }); 19 | 20 | jest.mock('../core', () => { 21 | return { 22 | createTransformer: ({input}) => { 23 | return input(); 24 | } 25 | }; 26 | }); 27 | const hexo = {}; 28 | const transformerPromise = require('../index')({hexo}); 29 | 30 | it('shouldn\'t emit any error', async () => { 31 | await expect(transformerPromise).resolves.toEqual('SWAGGER_OBJECT'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/response.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | <% if('default' === responseCode){ %> 5 | This is a default response for the request. 6 | <% }else{ %> 7 | Status Code: <%- responseCode %> 8 | <% } %> 9 |
10 | 11 | <% if(response.description){ %> 12 |
13 | <%- response.description %> 14 |
15 | <% } %> 16 | 17 |
18 |
19 | Reference 20 |
21 | <% if(response.data['__noData']){ %> 22 | <%- include_module('emptyResponse', { response: response.data }) %> 23 | <% } else { %> 24 | <%- include_module('responseBody', { response: response.data }) %> 25 | <% } %> 26 |
27 | 28 |
29 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/__tests__/lib/parseBody.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockInit = jest.fn(() => {}); 4 | 5 | jest.mock('../../lib/init', () => ({ 6 | init: mockInit 7 | })); 8 | 9 | describe('swagger-parser.lib.parseBody', () => { 10 | const {parseBody} = require('../../lib/parseBody'); 11 | 12 | describe('if body or body.schema is not defined', () => { 13 | const parsedBody = parseBody(); 14 | test('should return blank object', () => { 15 | expect(parsedBody).toEqual({}); 16 | }); 17 | }); 18 | 19 | describe('if body is defined', () => { 20 | const body = { 21 | schema: 'mockSchema' 22 | }; 23 | 24 | test('should traverse body.schema', () => { 25 | parseBody(body); 26 | expect(mockInit).toHaveBeenCalled(); 27 | expect(mockInit).toHaveBeenCalledWith({object: body.schema}); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /source/style/_swagger/swagger-ui-v2.scss: -------------------------------------------------------------------------------- 1 | .hexo-swagger-ui-v2 { 2 | // "namespace" required vendors 3 | @import "./swagger-ui-v2-vendors"; 4 | 5 | $font-size-small: 14px; 6 | $font-weight-big: 800; 7 | $font-weight-small: 300; 8 | 9 | .swagger-wrap.bootstrap { 10 | font-size: $font-size-small; 11 | font-weight: $font-weight-small; 12 | 13 | .api-infos-contact-url, li a { 14 | font-weight: $font-weight-small; 15 | } 16 | .content { 17 | margin-left: initial; 18 | } 19 | .endpoint-actions a { 20 | font-weight: $font-weight-small; 21 | font-size: $font-size-small; 22 | } 23 | .endpoint-heading a { 24 | font-weight: $font-weight-big; 25 | } 26 | 27 | .endpoint-heading > li, .endpoint-actions > li { 28 | margin-bottom: 0; 29 | } 30 | 31 | .api-version .h4 { 32 | font-weight: $font-weight-big; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/__tests__/response.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockParseResponse = jest.fn(() => 'flatResponse'); 4 | const mockHelpers = { 5 | parseResponse: mockParseResponse 6 | }; 7 | 8 | jest.mock('../../../helpers', () => mockHelpers); 9 | 10 | 11 | const responseController = require('../response'); 12 | 13 | describe('controllers.resposne', () => { 14 | it('should transform context as expected', () => { 15 | const ctx = { 16 | 'responseCode': 200, 17 | 'response': { 18 | description: 'foo' 19 | } 20 | }; 21 | 22 | const response = responseController(ctx); 23 | const expectedResponse = { 24 | description: 'foo', 25 | data: 'flatResponse' 26 | }; 27 | 28 | expect(Object.keys(response).length).toBe(2); 29 | expect(response.responseCode).toBe(200); 30 | expect(response.response).toEqual(expectedResponse); 31 | 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/operation.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

<%= operation.title %>

4 | 5 |
<%= operation.description %>
6 | 7 |
8 |
9 | <%=operation.verb.toUpperCase()%> 10 |
11 |
12 | <%=operation.path%> 13 |
14 |
15 |
16 | <% if(operation.security){ %> 17 |

Security Requirement

18 | <%- include_module('securityRequirement', { securityRequirement: operation.security }) %> 19 | <% } %> 20 | <%- include_module('request', { operation, baseUrl }) %> 21 | 22 | <%- include_module('responses', { operation }) %> 23 |
24 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/lib/processItems/object.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {traverse} = require('../traverse'); 4 | const {updateResult} = require('../updateResult'); 5 | 6 | const processObject = ({object, key, isRequired, result}) => { 7 | updateResult({object, key, isRequired, result}); 8 | 9 | // Check for additional properteis 10 | if (object.additionalProperties){ 11 | object = object.additionalProperties; 12 | } 13 | 14 | const properties = object.properties || {}; 15 | const required = object.required || [] ; 16 | 17 | return Object.keys(properties).reduce((result, propertyName) => { 18 | const property = properties[propertyName]; 19 | const childKey = key ? (key + '.' + propertyName) : propertyName; 20 | const isRequired = required.includes(propertyName); 21 | 22 | return traverse({ 23 | object: property, 24 | key: childKey, 25 | isRequired 26 | }); 27 | 28 | }, result); 29 | }; 30 | 31 | module.exports = processObject; 32 | -------------------------------------------------------------------------------- /banner.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const pkg = require('./package.json'); 5 | const path = require('path'); 6 | 7 | const banner = ` /*! 8 | * ${pkg.name} - ${pkg.version} 9 | * Copyright (c) see LICENSE at ${pkg.homepage}/blob/master/LICENSE 10 | */ 11 | `; 12 | 13 | const files = [ 14 | 'source/style/doc.css', 15 | 'source/style/swagger-ui-v2.css', 16 | 'source/style/swagger-ui-v3.css', 17 | 'source/script/doc.js' 18 | ]; 19 | 20 | files.forEach(writeBanner(banner)); 21 | 22 | function writeBanner (banner) { 23 | return function (filePath) { 24 | const fileExt = path.extname(filePath); 25 | const absFilePath = path.resolve(__dirname, filePath); 26 | let content = fs.readFileSync(absFilePath, 'utf8'); 27 | content = banner + content; 28 | if (fileExt === '.css') { 29 | content = content.replace(/@charset "UTF-8";/g, ''); 30 | content = '@charset "UTF-8";\n' + content; 31 | } 32 | fs.writeFileSync(absFilePath, content); 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/formData.ejs: -------------------------------------------------------------------------------- 1 | <% params.forEach((param) => {%> 2 |
3 | 16 |
17 | <%= `Used in ${param.in}` %> 18 |
19 |
20 | <%= param.description %> 21 |
22 | <% if ( param.example ) { %> 23 |
24 | Eg: <%= param.example %> 25 |
26 | <% } %> 27 |
28 | <% }) %> 29 | -------------------------------------------------------------------------------- /plugins/react-initial-state.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = ({hexo}) => { 4 | const allowedProperties = { 5 | config: [ 6 | 'root', 7 | 'theme', 8 | 'theme_config', 9 | 'time_format', 10 | 'timezone' 11 | ], 12 | page: [ 13 | 'path', 14 | 'title', 15 | 'support' 16 | ] 17 | }; 18 | 19 | const filter = (source, allowedValues) => { 20 | return Object 21 | .keys(source) 22 | .filter(key => allowedValues.includes(key)) 23 | .reduce((obj, key) => { 24 | obj[key] = source[key]; 25 | return obj; 26 | }, {}); 27 | }; 28 | 29 | hexo.extend.filter.register('template_locals', function (locals){ 30 | 31 | const data = locals.site.data; 32 | 33 | const page = filter(locals.page, allowedProperties.page); 34 | 35 | const config = filter(locals.config, allowedProperties.config); 36 | 37 | locals.initial_state = { 38 | page, 39 | data, 40 | config 41 | }; 42 | 43 | return locals; 44 | }, 20); 45 | }; 46 | -------------------------------------------------------------------------------- /lib/nodejs/hexo-util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {merge} = require('lodash'); 4 | 5 | module.exports = ({hexo}) => { 6 | return { 7 | /** 8 | * url_for helper that you can normally use in templates 9 | * but for programmatic usage in hexo plugins 10 | * 11 | * @type {function} 12 | */ 13 | url_for: hexo.extend.helper.get('url_for').bind({ 14 | config: hexo.config, 15 | relative_url: hexo.extend.helper.get('relative_url') 16 | }), 17 | /** 18 | * Get or merge theme_config 19 | * 20 | * @param {Object} object - the object that you wanna merge 21 | * @return {Object} current theme_config 22 | */ 23 | themeConfig: (object) => { 24 | if (!hexo.config.theme_config) { 25 | hexo.config.theme_config = {}; 26 | } 27 | 28 | if (!object) { 29 | return hexo.config.theme_config; 30 | } 31 | 32 | hexo.config.theme_config = merge({}, object, hexo.config.theme_config); 33 | 34 | return hexo.config.theme_config; 35 | } 36 | }; 37 | }; 38 | -------------------------------------------------------------------------------- /lib/nodejs/search/__tests__/child-on-message.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {mockLogger} = require('./mocks'); 4 | 5 | describe('search.child-on-message', () => { 6 | 7 | jest.mock('../build', () => () => 'test'); 8 | jest.mock('hexo-log', () => () => mockLogger); 9 | 10 | let childOnMessage, mockProcess; 11 | 12 | beforeEach(() => { 13 | mockProcess = { 14 | send: jest.fn() 15 | }; 16 | childOnMessage = require('../child-on-message')({ process: mockProcess }); 17 | }); 18 | 19 | it('should send a message back to parent process', () => { 20 | const message = { 21 | pages: { data: [] }, 22 | rootPath: '/' 23 | }; 24 | childOnMessage(message); 25 | 26 | expect(mockProcess.send).toHaveBeenCalledWith('test'); 27 | }); 28 | 29 | it('should send a message back to parent process also when pages is undefined', () => { 30 | const message = { 31 | rootPath: '/' 32 | }; 33 | childOnMessage(message); 34 | 35 | expect(mockProcess.send).toHaveBeenCalledWith('test'); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /lib/browser/__tests__/utils.test.js: -------------------------------------------------------------------------------- 1 | describe('browser.utils', () => { 2 | describe('url_for', () => { 3 | let url_for; 4 | beforeEach(() => { 5 | url_for = require('../utils').url_for({ 6 | config: { root: '/root' } 7 | }); 8 | }); 9 | 10 | it('should create a function', () => { 11 | expect(typeof url_for).toBe('function'); 12 | }); 13 | 14 | it('should join configured root with the path passed as an argument', () => { 15 | expect(url_for('/index.html?query=hello#hash')).toBe('/root/index.html?query=hello#hash'); 16 | }); 17 | 18 | it('should remove eventual double slashes', () => { 19 | const url_for = require('../utils').url_for({ 20 | config: { root: '/' } 21 | }); 22 | expect(url_for('//index.html?query=hello#hash')).toBe('/index.html?query=hello#hash'); 23 | }); 24 | 25 | it('should return the untouched path if it\'s an absolute url ', () => { 26 | const path = 'https://www.google.com'; 27 | expect(url_for(path)).toBe(path); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /source/style/_doc/search.scss: -------------------------------------------------------------------------------- 1 | .doc-search-form { 2 | max-width: 186px; 3 | 4 | &__input[type="search"] { 5 | width: 100%; 6 | box-sizing: border-box; 7 | } 8 | 9 | &__input:focus + &__btn { 10 | color: $dc-blue30; 11 | cursor: default; 12 | } 13 | 14 | &__btn { 15 | &:hover { 16 | color: $dc-gray40; 17 | cursor: default; 18 | } 19 | } 20 | } 21 | 22 | .doc-search-results { 23 | &__title { 24 | margin-top: 0; 25 | line-height: 3.4rem; 26 | 27 | &__query { 28 | font-weight: 500; 29 | } 30 | } 31 | 32 | &__list { 33 | list-style: none; 34 | padding: 0; 35 | 36 | &__item { 37 | display: block; 38 | } 39 | 40 | &__link { 41 | display: inline-block; 42 | font-weight: 500; 43 | } 44 | 45 | &__score-divider { 46 | display: inline-block; 47 | padding: 0 0.8rem; 48 | color: $dc-gray70; 49 | } 50 | 51 | &__score { 52 | color: $dc-gray50; 53 | } 54 | } 55 | 56 | .doc-highlight { 57 | font-weight: 500; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/lib/updateResult.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const updateResult = ({object, key, isRequired, result}) => { 4 | if (!key){ 5 | return false; 6 | } 7 | 8 | result[key] = result[key] || {}; 9 | 10 | const _result = result[key]; 11 | 12 | const description = object.description; 13 | const type = object.type; 14 | const values = object.values; // Used for enums 15 | const required = object.required || isRequired; 16 | const example = object.example; 17 | const format = object.format; 18 | 19 | if (description){ 20 | _result.description = description; 21 | } 22 | 23 | if (type){ 24 | _result.type = type; 25 | } 26 | 27 | if (values){ 28 | _result.values = values; 29 | } 30 | 31 | if (typeof required === 'boolean' && required){ 32 | _result.required = required; 33 | } 34 | 35 | if (example){ 36 | _result.example = example; 37 | } 38 | 39 | if (format){ 40 | _result.format = format; 41 | } 42 | 43 | return true; 44 | }; 45 | 46 | module.exports = {updateResult}; 47 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-ui/__tests__/mocks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockFileContent = { 4 | '/path/to/swagger/petstore.json': '{"swagger":"2.0","info":{"version":"1.0.0","title":"Swagger Petstore","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://swagger.io/terms/","contact":{"name":"Swagger API Team"},"license":{"name":"MIT"}},"host":"petstore.swagger.io","basePath":"/api","schemes":["http"],"consumes":["application/json"],"produces":["application/json"],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","produces":["application/json"],"responses":{"200":{"description":"A list of pets.","schema":{"type":"array","items":{"$ref":"#/definitions/Pet"}}}}}}},"definitions":{"Pet":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}', 5 | '/path/to/swagger/error.json': 'Invalid JSON' 6 | }; 7 | 8 | module.exports = { 9 | mockFileContent 10 | }; 11 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/__tests__/lib/processItems/array.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockTraverse = jest.fn(); 4 | const mockUpdateResult = jest.fn(); 5 | 6 | 7 | jest.mock('../../../lib/traverse', () => ({ 8 | traverse: mockTraverse 9 | })); 10 | 11 | jest.mock('../../../lib/updateResult', () => ({ 12 | updateResult: mockUpdateResult 13 | })); 14 | 15 | const processArray = require('../../../lib/processItems/array'); 16 | 17 | describe('swagger-parser.lib.processItems.array', () => { 18 | test('should process array', () => { 19 | const object = { 20 | items: ['foo', 'bar'] 21 | }; 22 | const key = 'key'; 23 | const isRequired = true; 24 | const result = {}; 25 | 26 | processArray({object, key, isRequired, result}); 27 | 28 | expect(mockUpdateResult).toHaveBeenCalled(); 29 | expect(mockUpdateResult).toHaveBeenCalledWith({object, key, isRequired, result}); 30 | 31 | expect(mockTraverse).toHaveBeenCalled(); 32 | expect(mockTraverse).toHaveBeenCalledWith({object: object.items , key: key + '[]'}); 33 | 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/__tests__/responseSample.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockHighlight = jest.fn(() => 'EXPECTED_SAMPLE'); 4 | 5 | jest.mock('../../../helpers', () => ({ 6 | highlight: mockHighlight 7 | })); 8 | 9 | const responseSampleController = require('../responseSample'); 10 | 11 | describe('controllers.responseSample', () => { 12 | it('should transform context as expected', () => { 13 | const responseData = { 14 | 'data': { 15 | }, 16 | 'links': { 17 | } 18 | }; 19 | const ctx = { 20 | sample: { 21 | 'application/json': responseData 22 | } 23 | }; 24 | 25 | const {sample} = responseSampleController(ctx); 26 | 27 | const sampleStr = JSON.stringify(ctx.sample['application/json'], null, 2); 28 | 29 | const highlightArgs = { 30 | code: sampleStr, 31 | lang: 'json' 32 | }; 33 | 34 | expect(mockHighlight).toHaveBeenCalled(); 35 | expect(mockHighlight).toHaveBeenCalledWith(highlightArgs); 36 | 37 | expect(sample).toEqual('EXPECTED_SAMPLE'); 38 | 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/operations.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const { toSlug } = require('../../helpers'); 5 | 6 | const operations = (ctx) => { 7 | const paths = ctx.paths; 8 | const globalSecurity = ctx.security; 9 | 10 | ctx.schemes = ctx.schemes || []; 11 | 12 | let baseUrl = ''; 13 | if (ctx.schemes.includes('https')){ 14 | baseUrl += 'https://'; 15 | } else { 16 | baseUrl += 'http://'; 17 | } 18 | baseUrl += ctx.host + ctx.basePath; 19 | 20 | const operations = Object.keys(paths).reduce((acc, path_) => { 21 | const ops = Object.keys(paths[path_]).map((verb) => { 22 | const operation = paths[path_][verb]; 23 | const title = operation.summary || operation.operationId || operation.description || ''; 24 | return Object.assign({}, operation, { 25 | verb, 26 | path: path_, 27 | title: title, 28 | id: toSlug(title) 29 | }); 30 | }); 31 | return acc.concat(ops); 32 | }, []); 33 | 34 | return { 35 | operations, 36 | baseUrl, 37 | globalSecurity 38 | }; 39 | }; 40 | 41 | 42 | module.exports = operations; 43 | -------------------------------------------------------------------------------- /plugins/search.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const generator = require('../lib/nodejs/search/generator'); 4 | const util = require('../lib/nodejs/hexo-util'); 5 | 6 | const DEFAULT_CONFIG = { route: '/lunr.json' }; 7 | 8 | module.exports = ({hexo}) => { 9 | const {themeConfig} = util({hexo}); 10 | hexo.extend.generator.register('search', createGeneratorFn({hexo, themeConfig})); 11 | }; 12 | 13 | function createGeneratorFn ({hexo, themeConfig}) { 14 | const cmd = hexo.env.args._ && hexo.env.args._.length ? hexo.env.args._[0] : null; 15 | 16 | // hexo commands that should activate the generator 17 | const cmds = [ 18 | 'generate', 19 | 'server', 20 | 'deploy', 21 | 'g', 22 | 's', 23 | 'd' 24 | ]; 25 | 26 | // hexo commands that should activate the generator in background mode 27 | const bgCmds = [ 28 | 'server', 29 | 's' 30 | ]; 31 | 32 | const skip = cmds.indexOf(cmd) === -1 && typeof hexo.env.args._ !== 'undefined'; 33 | const background = bgCmds.indexOf(cmd) > -1; 34 | 35 | themeConfig({ search: { skip, background, route: DEFAULT_CONFIG.route } }); 36 | 37 | return generator({hexo}); 38 | } 39 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/emptyResponse.ejs: -------------------------------------------------------------------------------- 1 | <% if(response.headers){ %> 2 |

Headers

3 | <% 4 | const headers = response.headers; 5 | Object.keys(headers).forEach((key) => { 6 | const header = headers[key]; 7 | header.name = key; 8 | %> 9 |
10 | 20 | <% if(header.description){ %> 21 |
22 | <%= header.description %> 23 |
24 | <% } %> 25 | <% if(header.format){ %> 26 |
27 | <%= header.format %> 28 |
29 | <% } %> 30 | 31 |
32 | <% }) %> 33 | <% } %> 34 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/__tests__/lib/parseResponse.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockInit = jest.fn(() => {}); 4 | 5 | jest.mock('../../lib/init', () => ({ 6 | init: mockInit 7 | })); 8 | 9 | describe('swagger-parser.lib.parseResponse', () => { 10 | const {parseResponse} = require('../../lib/parseResponse'); 11 | 12 | describe('if response not defined', () => { 13 | const parsedResponse = parseResponse(); 14 | test('should return blank object', () => { 15 | expect(parsedResponse).toEqual({}); 16 | }); 17 | }); 18 | 19 | describe('if response is defined', () => { 20 | describe('if response have schema', () => { 21 | const response = { 22 | schema: 'mockSchema' 23 | }; 24 | parseResponse(response); 25 | expect(mockInit).toHaveBeenCalled(); 26 | expect(mockInit).toHaveBeenCalledWith({object: response.schema}); 27 | }); 28 | 29 | describe('if response does not have schema', () => { 30 | const response = {}; 31 | const parsedResponse = parseResponse(response); 32 | expect(parsedResponse).toEqual({ '__noData': true}); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const stripIndent = require('strip-indent'); 3 | const hexoUtil = require('hexo-util'); 4 | const hexoHighlight = hexoUtil.highlight; 5 | 6 | 7 | const {mergeWith, isArray, uniq} = require('lodash'); 8 | 9 | const arrayMergeCustomizer = (objValue, srcValue) => { 10 | if (isArray(objValue)) { 11 | return uniq(objValue.concat(srcValue)); 12 | } 13 | }; 14 | 15 | const deepMerge = (object, sources, customizer) => { 16 | const finalCustomizer = customizer || arrayMergeCustomizer; 17 | return mergeWith(object, sources, finalCustomizer); 18 | }; 19 | 20 | 21 | const toSlug = (words) => { 22 | return encodeURIComponent(words.replace(/\s/g, '-').replace(/\./g, '')); 23 | }; 24 | 25 | const highlight = ({code, lang}) => { 26 | const config = { 27 | gutter: false, 28 | firstLine: false 29 | }; 30 | 31 | if (lang){ 32 | config.lang = lang; 33 | } else { 34 | config.autoDetect = true; 35 | } 36 | 37 | code = stripIndent(code).trim(); 38 | return hexoHighlight(code, config); 39 | }; 40 | 41 | module.exports = { 42 | deepMerge, 43 | toSlug, 44 | highlight 45 | }; 46 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/__tests__/swagger-unmocked.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | 5 | describe('swager.Swagger.parser', () => { 6 | const specFile = 'lib/nodejs/swagger/__tests__/_petstore.yaml'; 7 | const expectedSpec = 'lib/nodejs/swagger/__tests__/_petstore-parsed.yaml'; 8 | 9 | const Swagger = require('../swagger'); 10 | const decorators = require('../decorators'); 11 | const swagger = new Swagger(specFile); 12 | 13 | it('should generate xml schema', () => { 14 | swagger 15 | .validate() 16 | .then(swagger => swagger.decorate(decorators.docExclude)) 17 | .then(swagger => swagger.decorate(decorators.host)) 18 | .then(swagger => { 19 | const readableStream = fs.createReadStream(expectedSpec); 20 | let data = ''; 21 | 22 | readableStream 23 | .on('readable', () => { 24 | let chunk; 25 | while ((chunk = readableStream.read()) !== null) { 26 | data += chunk; 27 | } 28 | }) 29 | .on('end', () => { 30 | expect(data).toEqual(swagger.swaggerYaml); 31 | }); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-ui/partials/v2/snippet.ejs: -------------------------------------------------------------------------------- 1 |
3 |
12 | download="<%- options.download %>" 13 | <% } %> 14 | > 15 |
16 |
17 | 36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | group: deprecated-2017Q4 4 | 5 | cache: 6 | directories: 7 | - node_modules 8 | 9 | language: node_js 10 | 11 | node_js: 12 | - '12' 13 | 14 | addons: 15 | chrome: stable 16 | 17 | before_script: 18 | - npm link 19 | 20 | # prepare e2e test environment 21 | - "export DISPLAY=:99.0" 22 | - "sh -e /etc/init.d/xvfb start" # the starting the virtual X frame buffer: Xvfb process 23 | 24 | - git clone --depth=50 --branch=gh-pages-source https://github.com/zalando-incubator/hexo-theme-doc ../hexo-theme-doc-site 25 | - cd ../hexo-theme-doc-site && npm install -q && npm link hexo-theme-doc 26 | - cd $TRAVIS_BUILD_DIR 27 | 28 | script: 29 | # quality checks, unit test and compiling fe artifacts 30 | - npm run lint -s 31 | - npm run lint:report -s 32 | - npm run test:coverage -s 33 | - npm run compile -s 34 | 35 | # run e2e tests 36 | - cd ../hexo-theme-doc-site && npm run generate 37 | - npm run serve &>/dev/null & 38 | - sleep 5 # give server some time to start 39 | - npm test 40 | - cd $TRAVIS_BUILD_DIR 41 | 42 | after_success: 43 | cat target/coverage/lcov.info | ./node_modules/.bin/codacy-coverage 44 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/request.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Request

5 |
6 |
7 |

Sample Request

8 |
9 |
10 | 11 | 12 | 29 | 32 | 33 |
13 |
14 | 15 | <% if( request.header.length ) {%> 16 | <%- include_module('header', { header: request.header }) %> 17 | <% } %> 18 | 19 | <% if( request.path.length || request.query.length ) {%> 20 | <%- include_module('requestParams', { params: request.path.concat(request.query) }) %> 21 | <% } %> 22 | 23 | <% if( request.formData.length || request.body.length) {%> 24 | <%- include_module('requestContent', {request}) %> 25 | <% } %> 26 | 27 |
28 |
30 | <%- include_module('requestSample', {request, path, verb, baseUrl}) %> 31 |
34 |
35 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockFilterSecurity = jest.fn(swagger => swagger + '-SECURITY'); 4 | const mockFilterParameters = jest.fn(swagger => swagger + '-PARAMETERS'); 5 | const mockFilterPaths = jest.fn(swagger => swagger + '-PATHS'); 6 | const mockFilterOperations = jest.fn(swagger => swagger + '-OPERATIONS'); 7 | 8 | jest.mock('../filters', () => ({ 9 | filterSecurity: mockFilterSecurity, 10 | filterParameters: mockFilterParameters, 11 | filterPaths: mockFilterPaths, 12 | filterOperations: mockFilterOperations 13 | })); 14 | 15 | describe('docExclude.index', () => { 16 | 17 | it('should apply the filters', () => { 18 | const docExclude = require('../index'); 19 | 20 | const swagger = 'SWAGGER'; 21 | 22 | const updatedSwagger = docExclude(swagger); 23 | 24 | const expectedSwagger = 'SWAGGER-SECURITY-PARAMETERS-PATHS-OPERATIONS'; 25 | 26 | expect(mockFilterSecurity).toBeCalled(); 27 | expect(mockFilterParameters).toBeCalled(); 28 | expect(mockFilterPaths).toBeCalled(); 29 | expect(mockFilterOperations).toBeCalled(); 30 | 31 | expect(updatedSwagger).toBe(expectedSwagger); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/header.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 | Header 4 |
5 | <% header.forEach((param) => {%> 6 |
7 | 22 | <% if ( param.description ) { %> 23 |
24 | <%= param.description %> 25 |
26 | <% } %> 27 | <% if ( param.example ) { %> 28 |
29 | Eg: <%= param.example %> 30 |
31 | <% } %> 32 |
33 | <% }) %> 34 |
35 | 36 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/__tests__/operation.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const operationController = require('../operation'); 4 | 5 | describe('controllers.operation', () => { 6 | test('should transform the context as expected', () => { 7 | const ctx = { 8 | operation: { 9 | security: { 10 | items: ['a'] 11 | } 12 | }, 13 | globalSecurity: { 14 | items: ['b'], 15 | foo: 'bar' 16 | } 17 | }; 18 | 19 | const updatedCtx = operationController(ctx); 20 | const expectedSecurity = { 21 | foo: 'bar', 22 | items: ['a', 'b'] 23 | }; 24 | 25 | expect(updatedCtx.operation.security).toEqual(expectedSecurity); 26 | }); 27 | test('should handle duplicates', () => { 28 | const ctx = { 29 | operation: { 30 | security: { 31 | items: ['a', 'b', 'c'] 32 | } 33 | }, 34 | globalSecurity: { 35 | items: ['b', 'd'], 36 | foo: 'bar' 37 | } 38 | }; 39 | 40 | const updatedCtx = operationController(ctx); 41 | const expectedSecurity = { 42 | foo: 'bar', 43 | items: ['a', 'b', 'c', 'd'] 44 | }; 45 | 46 | expect(updatedCtx.operation.security).toEqual(expectedSecurity); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/filters/__tests__/parameters.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const parametersFilter = require('../parameters'); 4 | 5 | const dummySwagger = { 6 | parameters: { 7 | 'foo':{ 8 | 'name': 'foo' 9 | }, 10 | 'bar':{ 11 | 'name': 'bar', 12 | 'x-doc': { 13 | 'excluded': true 14 | } 15 | } 16 | }, 17 | paths: { 18 | '/pets': { 19 | 'get': { 20 | parameters:[ 21 | { 22 | name: 'param1' 23 | }, 24 | { 25 | name: 'param2', 26 | 'x-doc':{ 27 | 'excluded': true 28 | } 29 | } 30 | ] 31 | } 32 | } 33 | } 34 | }; 35 | 36 | const expectedSwagger = { 37 | parameters: { 38 | 'foo':{ 39 | 'name': 'foo' 40 | } 41 | }, 42 | paths: { 43 | '/pets': { 44 | 'get': { 45 | parameters:[ 46 | { 47 | 'name': 'param1' 48 | } 49 | ] 50 | } 51 | } 52 | } 53 | }; 54 | 55 | describe('docExclude.parameters', () => { 56 | it('should filter parameters', () => { 57 | const updatedSwagger = parametersFilter(dummySwagger); 58 | 59 | expect(updatedSwagger).toEqual(expectedSwagger); 60 | }); 61 | }); 62 | 63 | -------------------------------------------------------------------------------- /source/style/_doc/layout.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 16px; 3 | overflow-y: auto; 4 | overflow-x: hidden; 5 | 6 | &.doc-navigation--is-collapsed { 7 | @media screen and (min-width: $doc-breakpoint) { 8 | .doc-navbar { 9 | width: $doc-navbar-width-collapsed; 10 | overflow: hidden; 11 | border-bottom: 1px solid $doc-navbar-background-color; 12 | } 13 | 14 | .doc-sidebar { 15 | transform: translateX(-($doc-sidebar-width - $doc-navbar-width-collapsed)); 16 | } 17 | 18 | .doc-content { 19 | transform: translateX($doc-sidebar-width-collapsed); 20 | margin-right: $doc-sidebar-width-collapsed; 21 | } 22 | 23 | .doc-navbar__logo__text, 24 | .doc-sidebar-content, 25 | .doc-navbar__sidebar-close { 26 | display: none; 27 | } 28 | 29 | .doc-sidebar__vertical-menu { 30 | display: block; 31 | } 32 | } 33 | } 34 | 35 | &.doc-sidebar--is-visible { 36 | @media screen and (max-width: $doc-breakpoint) { 37 | .doc-sidebar { 38 | transform: translateX(0); 39 | } 40 | 41 | .doc-content { 42 | transform: translateX($doc-sidebar-width); 43 | } 44 | } 45 | } 46 | } 47 | 48 | .doc-sidebar__vertical-menu { 49 | display: none; 50 | } 51 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/filters/__tests__/security.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const securityFilter = require('../security'); 4 | 5 | const dummySwagger = { 6 | securityDefinitions:{ 7 | 'oauth':{ 8 | 9 | }, 10 | 'internalAuth':{ 11 | 'x-doc': { 12 | 'excluded': true 13 | } 14 | }, 15 | 'foo': { 16 | 'x-doc': { 17 | 'excluded': true 18 | } 19 | } 20 | }, 21 | security: [ 22 | { 23 | 'oauth':[] 24 | }, 25 | { 26 | 'internalAuth': [] 27 | } 28 | ], 29 | paths: { 30 | '/pets': { 31 | 'get': { 32 | }, 33 | 'post': { 34 | security: [ 35 | { 36 | 'foo': [] 37 | } 38 | ] 39 | } 40 | } 41 | } 42 | }; 43 | 44 | const expectedSwagger = { 45 | securityDefinitions:{ 46 | 'oauth':{ 47 | 48 | } 49 | }, 50 | security: [ 51 | { 52 | 'oauth':[] 53 | } 54 | ], 55 | paths: { 56 | '/pets': { 57 | 'get': { 58 | }, 59 | 'post': { 60 | security: [] 61 | } 62 | } 63 | } 64 | }; 65 | 66 | describe('docExclude.parameters', () => { 67 | it('should filter parameters', () => { 68 | const updatedSwagger = securityFilter(dummySwagger); 69 | 70 | expect(updatedSwagger).toEqual(expectedSwagger); 71 | }); 72 | }); 73 | 74 | -------------------------------------------------------------------------------- /plugins/react.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // NOTE: "node-jsx" package is deprecated but it's only one 4 | // that is working correctly without going crazy with presets and babel 5 | // the correct solution should be: 6 | // 7 | // ``` 8 | // require('babel-register')({ 9 | // 'presets': ['react', 'es2015'] 10 | // }); 11 | // ``` 12 | // 13 | // But for "yet" unknown reasons it works just when you `npm link` the package but not when 14 | // you install it in a project with the usual `npm install`... ¯\_(ツ)_/¯ 15 | // 16 | 17 | 18 | require('node-jsx').install(); 19 | 20 | const React = require('react'); 21 | const ReactDOM = require('react-dom/server'); 22 | const {Navigation} = require('../lib/browser/navigation/containers.jsx'); 23 | const components = { 24 | Navigation 25 | }; 26 | 27 | module.exports = ({hexo}) => { 28 | 29 | /** 30 | * "Server-side render" a React component 31 | * @param {String} componentName - The componentName 32 | * @param {Object} [props={}] - injected props 33 | * @return {string} 34 | */ 35 | function reactComponent (componentName, props = {}) { 36 | const Component = components[componentName]; 37 | const componentFactory = React.createFactory(Component); 38 | return ReactDOM.renderToString(componentFactory(props)); 39 | } 40 | 41 | hexo.extend.helper.register('react_component', reactComponent); 42 | }; 43 | -------------------------------------------------------------------------------- /lib/browser/search/__tests__/load.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | 5 | /* global global */ 6 | const mockSearcher = () => { 7 | return () => {}; 8 | }; 9 | 10 | jest.mock('../../../nodejs/search/searcher', () => mockSearcher); 11 | 12 | const mockLunr = { 13 | Index: { 14 | load: (index) => index 15 | } 16 | }; 17 | 18 | jest.mock('lunr', () => mockLunr); 19 | 20 | const mockResponse = () => { 21 | return { 22 | json: () => Promise.resolve({ 23 | index: {}, 24 | store: {} 25 | }) 26 | }; 27 | }; 28 | 29 | const mockFetch = jest.fn().mockReturnValue(Promise.resolve(mockResponse())); 30 | 31 | global.fetch = mockFetch; 32 | 33 | describe('browser.search.load', () => { 34 | const load = require('../load'); 35 | 36 | it('should fetch the expected json file and return a search function', () => { 37 | return load('/foo.json').then((search) => { 38 | expect(mockFetch).toHaveBeenCalledWith('/foo.json', { credentials: 'include'} ); 39 | expect(typeof search).toBe('function'); 40 | }); 41 | }); 42 | 43 | it('should fetch the default json file and return a search function', () => { 44 | return load().then((search) => { 45 | expect(mockFetch).toHaveBeenCalledWith('/lunr.json', { credentials: 'include'} ); 46 | expect(typeof search).toBe('function'); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/lib/traverse.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {merge} = require('lodash'); 4 | let result; 5 | 6 | function traverse ({object, key, isRequired, init}){ 7 | 8 | if (!object){ 9 | return {}; 10 | } 11 | // handle allOf property 12 | if (object.allOf){ 13 | let mergedObj = {type: 'object'}; 14 | for (const obj of object.allOf){ 15 | mergedObj = merge(mergedObj, obj); 16 | } 17 | object = mergedObj; 18 | 19 | // delete allOf as its already used 20 | delete object.allOf; 21 | } 22 | 23 | // Check object type. 24 | const type = object.type; 25 | const hasProperties = !!object.properties; 26 | 27 | if (init){ 28 | result = {}; 29 | } 30 | 31 | if ('array' === type){ 32 | return processArray({object, key, isRequired, result}); 33 | } 34 | 35 | // Some schemas, object does not have a type but have properties 36 | if ('object' === type || hasProperties){ 37 | return processObject({object, key, isRequired, result}); 38 | } 39 | 40 | if (object.enum){ 41 | return processEnum({object, key, isRequired, result}) ; 42 | } 43 | 44 | return processDefault({object, key, isRequired, result}); 45 | } 46 | 47 | 48 | 49 | module.exports = { 50 | traverse 51 | }; 52 | 53 | const {processArray, processObject, processEnum, processDefault} = require('./processItems') ; 54 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/responseBody.ejs: -------------------------------------------------------------------------------- 1 | <% 2 | Object.keys(response).forEach((key) => { 3 | const value = response[key]; 4 | value.name = key; 5 | %> 6 |
7 | 22 | <% if(value.description){ %> 23 |
24 | <%= value.description %> 25 |
26 | <% } %> 27 | <% if(value.values){ %> 28 |
29 | <%- 'Allowed values: ' + value.values.join(', ') + '.' %> 30 |
31 | <% } %> 32 | <% if(value.example){ %> 33 |
34 | Eg. <%= value.example %> 35 |
36 | <% } %> 37 |
38 | <% }); %> 39 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/requestParams.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 | Path 4 |
5 | <% params.forEach((param) => {%> 6 |
7 | 22 | <% if ( param.in ) { %> 23 |
24 | <%= `Used in ${param.in}` %> 25 |
26 | <% } %> 27 | <% if ( param.description ) { %> 28 |
29 | <%= param.description %> 30 |
31 | <% } %> 32 | <% if ( param.example ) { %> 33 |
34 | Eg: <%= param.example %> 35 |
36 | <% } %> 37 |
38 | <% }) %> 39 |
40 | 41 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/filters/parameters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { isExclude } = require('../utils'); 4 | const { filter } = require('lodash'); 5 | 6 | 7 | /** 8 | * Filter parameters. 9 | */ 10 | const filterParameters = (swagger) => { 11 | 12 | const paramsToRemove = []; 13 | // Filter swager parameter definitions 14 | swagger.parameters && Object 15 | .keys(swagger.parameters) 16 | .forEach((key) => { 17 | const value = swagger.parameters[key]; 18 | if (isExclude(value)){ 19 | paramsToRemove.push(value.name); 20 | delete swagger.parameters[key]; 21 | } 22 | }); 23 | 24 | 25 | /** 26 | * Filter params for operations 27 | * */ 28 | swagger.paths && Object 29 | .keys(swagger.paths) 30 | .forEach((key) => { 31 | const path = swagger.paths[key]; 32 | path && Object 33 | .keys(path) 34 | .forEach((verb) => { 35 | const operation = path[verb]; 36 | if ('object' !== typeof operation){ 37 | return; 38 | } 39 | 40 | const parameters = operation.parameters; 41 | if (Array.isArray(parameters)){ 42 | operation.parameters = filter(parameters, parameter => !paramsToRemove.includes(parameter.name) && !isExclude(parameter)); 43 | } 44 | }); 45 | }); 46 | 47 | return swagger; 48 | }; 49 | 50 | 51 | module.exports = filterParameters; 52 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/requestBody.ejs: -------------------------------------------------------------------------------- 1 | <% body.forEach((bodyObject) => { 2 | Object.keys(bodyObject).forEach((key) => { 3 | const value = bodyObject[key]; 4 | value.name = key; 5 | %> 6 |
7 | 22 | <% if(value.description){ %> 23 |
24 | <%= value.description %> 25 |
26 | <% } %> 27 | <% if(value.values){ %> 28 |
29 | <%- 'Allowed values: ' + value.values.join(', ') + '.' %> 30 |
31 | <% } %> 32 | <% if(value.example){ %> 33 |
34 | Eg. <%= value.example %> 35 |
36 | <% } %> 37 |
38 | <% 39 | }); 40 | }); 41 | %> 42 | 43 | 44 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/__tests__/operations.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const operationsController = require('../operations'); 4 | 5 | describe('controllers.operations', () => { 6 | it('should transform the context as expected', () => { 7 | const ctx = { 8 | paths: { 9 | '/foo/{fooId}': { 10 | 'get': { 11 | summary: 'Get Foo' 12 | }, 13 | 'post': { 14 | summary: 'Post Foo' 15 | } 16 | }, 17 | '/bar/{barId}': { 18 | 'get': { 19 | summary: 'Get Bar' 20 | } 21 | }, 22 | }, 23 | host: 'example.com', 24 | basePath: '/', 25 | schemes: ['https'] 26 | }; 27 | 28 | const {operations, baseUrl} = operationsController(ctx); 29 | 30 | expect(operations).toBeInstanceOf(Array); 31 | expect(operations.length).toBe(3); 32 | 33 | expect(operations[0].title).toBe('Get Foo'); 34 | expect(operations[0].verb).toBe('get'); 35 | expect(operations[0].path).toBe('/foo/{fooId}'); 36 | 37 | expect(operations[1].title).toBe('Post Foo'); 38 | expect(operations[1].verb).toBe('post'); 39 | expect(operations[1].path).toBe('/foo/{fooId}'); 40 | 41 | expect(operations[2].title).toBe('Get Bar'); 42 | expect(operations[2].verb).toBe('get'); 43 | expect(operations[2].path).toBe('/bar/{barId}'); 44 | 45 | expect(baseUrl).toBe('https://example.com/'); 46 | 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /layout/layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%- config.title %> | <%- page.title %> 7 | 8 | 9 | 10 | <%- project_partial('head_start') %> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | <%- project_partial('head_end') %> 23 | 24 | 25 | 26 | 27 | 28 | 29 | <%- partial('_partial/navigation') -%> 30 | <%- body %> 31 | <%- partial('_partial/google_analytics') %> 32 | 33 | <%- project_partial('footer_start') %> 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | <%- project_partial('footer_end') %> 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /lib/browser/search/containers.jsx: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const $ = require('jquery'); 3 | const {SHOW_SEARCH_RESULTS, HIDE_SEARCH_RESULTS} = require('./actions'); 4 | const {subscribeOn} = require('../utils'); 5 | const {SearchResultsTitle, SearchResultsList} = require('./components.jsx'); 6 | const {SupportFooter} = require('../support/components.jsx'); 7 | 8 | class SearchResults extends React.Component { 9 | constructor (props) { 10 | super(props); 11 | this.$page = $('#page-content'); 12 | this.state = { 13 | query: null, 14 | visible: false, 15 | results: [] 16 | }; 17 | } 18 | 19 | componentDidMount () { 20 | 21 | subscribeOn(SHOW_SEARCH_RESULTS, (e) => { 22 | this.$page.hide(); 23 | this.setState({ 24 | query: e.query, 25 | visible: true, 26 | results: e.results 27 | }); 28 | window.scrollTo(0,0); 29 | }); 30 | 31 | subscribeOn(HIDE_SEARCH_RESULTS, () => { 32 | this.$page.show(); 33 | this.setState({ 34 | query: null, 35 | visible: false, 36 | results: [] 37 | }); 38 | }); 39 | } 40 | 41 | render () { 42 | if (!this.state.visible) { return null; } 43 | 44 | const {theme_config} = this.props.config; 45 | 46 | return ( 47 |
48 | 49 | 50 | { theme_config.support ? : null } 51 |
52 | ); 53 | } 54 | } 55 | 56 | module.exports = {SearchResults}; 57 | -------------------------------------------------------------------------------- /lib/nodejs/support/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {merge} = require('lodash'); 4 | 5 | describe('support', () => { 6 | const {filter, DEFAULT_CONFIG} = require('../index'); 7 | describe('filter', () => { 8 | it('when font-matter page.support property is equal to false, it should leave untouched the locals object', () => { 9 | const locals = { 10 | page: { support: false } 11 | }; 12 | const actual = filter(locals); 13 | 14 | expect(Object.keys(actual)).toEqual(Object.keys(locals)); 15 | expect(actual.page.support).toEqual(locals.page.support); 16 | }); 17 | 18 | it('when theme_config.support property is equal to a falsy value or undefined, it should leave untouched the locals object', () => { 19 | const locals = { 20 | page: { }, 21 | config: { 22 | theme_config: { } 23 | } 24 | }; 25 | const actual = filter(locals); 26 | 27 | expect(Object.keys(actual)).toEqual(Object.keys(locals)); 28 | expect(actual.page.support).toEqual(locals.page.support); 29 | }); 30 | 31 | it('should merge defaults to user provided support config', () => { 32 | const locals = { 33 | page: { }, 34 | config: { 35 | theme_config: { 36 | support: { 37 | 'link_text': 'Hello' 38 | } 39 | } 40 | } 41 | }; 42 | const actual = filter(locals); 43 | const expected = merge({}, DEFAULT_CONFIG, { link_text: 'Hello'}); 44 | 45 | expect(actual.page.support).toEqual(expected); 46 | expect(actual.config.theme_config.support).toEqual(expected); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-ui/__tests__/parse-schema-file.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const ParseSchemaFileError = require('../parse-schema-file-error.js'); 4 | 5 | describe('parse-schema-file', () => { 6 | 7 | const mockGetSwagger = jest.fn() 8 | .mockImplementationOnce(() => { 9 | return Promise.resolve({ 10 | swagger: { 11 | swaggerJson: 'SWAGGER' 12 | } 13 | }); 14 | }) 15 | .mockImplementationOnce(() => { 16 | return Promise.reject(new ParseSchemaFileError({ 17 | 'message': 'There is an error reading the file.', 18 | 'filePath': '/path/to/swagger/petstore.json', 19 | 'referencePath': 'path/to/md/file' 20 | })); 21 | }); 22 | 23 | jest.mock('../../swagger-store', () => { 24 | return () => ({ 25 | getSwagger: mockGetSwagger 26 | }); 27 | }); 28 | 29 | const parseSchemaFile = require('../parse-schema-file'); 30 | 31 | test('should parse the schema file', async () => { 32 | const swaggerFilePath = '/path/to/swagger/petstore.json'; 33 | const mdFilePath = 'path/to/md/file'; 34 | const expectedOutput = { 35 | 'pageSource': mdFilePath, 36 | 'swagger': 'SWAGGER' 37 | }; 38 | const hexo = {}; 39 | await expect(parseSchemaFile(swaggerFilePath, mdFilePath, hexo)).resolves.toEqual(expectedOutput); 40 | expect(mockGetSwagger).toBeCalled(); 41 | }); 42 | 43 | test('should catch file read error for file read issues', async () => { 44 | const swaggerFilePath = '/path/to/swagger/petstore.json'; 45 | const mdFilePath = 'path/to/md/file'; 46 | await expect(parseSchemaFile(swaggerFilePath, mdFilePath)).rejects.toBeInstanceOf(ParseSchemaFileError); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /lib/nodejs/__tests__/hexo-util.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Hexo = require('hexo'); 4 | const hexo = new Hexo(); 5 | const hexoUtil = require('../hexo-util'); 6 | const mockRoute = 'mockRoute'; 7 | const mockUrlFor = jest.fn().mockImplementation(route => '/' + route); 8 | 9 | 10 | hexo.config = { 11 | relative_link: 'relative_link' 12 | }; 13 | hexo.extend.helper.store['url_for'] = mockUrlFor; 14 | 15 | describe('hexo-util', () => { 16 | 17 | const {url_for, themeConfig} = hexoUtil({hexo}); 18 | 19 | describe('url_for', () => { 20 | const url = url_for(mockRoute); 21 | test('should call native url_for method', () => { 22 | expect(url).toEqual('/' + mockRoute); 23 | }); 24 | }); 25 | 26 | describe('themeConfig', () => { 27 | 28 | it('should initialize `theme_config` if undefined', () => { 29 | expect(hexo.config.theme_config).toBeUndefined(); 30 | themeConfig(); 31 | expect(hexo.config.theme_config).toBeDefined(); 32 | }); 33 | 34 | it('should return `theme_config` as the result of a deep merge with the object passed as an argument and the current `theme_config`', () => { 35 | hexo.config.theme_config = { 36 | foo: { 37 | bar: 'hello' 38 | }, 39 | john: 'doe' 40 | }; 41 | const defaults = { 42 | foo: { 43 | bar: 'world' 44 | }, 45 | search: true 46 | }; 47 | 48 | const actual = themeConfig(defaults); 49 | const expected = { 50 | foo: { 51 | bar: 'hello' 52 | }, 53 | john: 'doe', 54 | search: true, 55 | }; 56 | 57 | expect(actual).toEqual(expected); 58 | expect(hexo.config.theme_config).toEqual(expected); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/decorators/docExclude/filters/security.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { isExclude } = require('../utils'); 4 | 5 | 6 | /** 7 | * Filteres securitDefinitions and security requirements. 8 | */ 9 | const filterSecurity = (swagger) => { 10 | 11 | const securityToRemove = []; 12 | 13 | const securityDefinitions = swagger.securityDefinitions; 14 | 15 | const filterSecurityRequirements = (securityArr) => { 16 | return securityArr.reduce((acc, curr) => { 17 | const key = Object.keys(curr)[0]; 18 | if (!securityToRemove.includes(key)){ 19 | acc.push(curr); 20 | } 21 | return acc; 22 | }, 23 | []); 24 | }; 25 | 26 | // Remove security definitions 27 | securityDefinitions && Object 28 | .keys(securityDefinitions) 29 | .forEach((key) => { 30 | const value = securityDefinitions[key]; 31 | if (isExclude(value)){ 32 | securityToRemove.push(key); 33 | delete securityDefinitions[key]; 34 | } 35 | }); 36 | 37 | // Remove security requirements 38 | if (securityToRemove.length){ 39 | 40 | // Global security 41 | if (swagger.security){ 42 | swagger.security = filterSecurityRequirements(swagger.security); 43 | } 44 | 45 | // Local security 46 | swagger.paths && Object 47 | .keys(swagger.paths) 48 | .forEach((key) => { 49 | const path = swagger.paths[key]; 50 | path && Object 51 | .keys(path) 52 | .forEach((verb) => { 53 | const operation = path[verb]; 54 | if (operation.security){ 55 | operation.security = filterSecurityRequirements(operation.security); 56 | } 57 | }); 58 | }); 59 | } 60 | 61 | return swagger; 62 | }; 63 | 64 | module.exports = filterSecurity; 65 | -------------------------------------------------------------------------------- /lib/browser/search/__tests__/containers.test.jsx: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const {shallow, mount} = require('enzyme'); 3 | const {dispatch} = require('../../utils'); 4 | 5 | const {HIDE_SEARCH_RESULTS, SHOW_SEARCH_RESULTS} = require('../actions'); 6 | 7 | describe('browser.search.containers', () => { 8 | 9 | beforeEach(() => { 10 | document.documentElement.innerHTML = ` 11 |
12 | `; 13 | }); 14 | 15 | describe('SearchResults', () => { 16 | const {SearchResults} = require('../containers'); 17 | 18 | it('in initial state it should not be visible', () => { 19 | const results = shallow(); 20 | expect(results.getElement()).toBe(null); 21 | }); 22 | 23 | it('when SHOW_SEARCH_RESULTS action is triggered, should be visible', () => { 24 | const results = mount(); 25 | dispatch(SHOW_SEARCH_RESULTS, { results: [] }); 26 | expect(results.getElement()).not.toBe(null); 27 | }); 28 | 29 | it('when SHOW_SEARCH_RESULTS action is triggered, it should hide page content', () => { 30 | const results = mount(); 31 | expect(results.instance().$page.css('display')).toBe('block'); 32 | dispatch(SHOW_SEARCH_RESULTS, { results: [] }); 33 | expect(results.instance().$page.css('display')).toBe('none'); 34 | }); 35 | 36 | it('when HIDE_SEARCH_RESULTS action is triggered, it should not be visible and the page content is visible', () => { 37 | const results = mount(); 38 | dispatch(SHOW_SEARCH_RESULTS, { results: [] }); 39 | expect(results.instance().$page.css('display')).toBe('none'); 40 | 41 | dispatch(HIDE_SEARCH_RESULTS); 42 | expect(results.instance().$page.css('display')).toBe('block'); 43 | expect(results.find('div').length).toBe(0); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/__tests__/lib/updateResult.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | describe('swagger-parser.lib.updateResult', () => { 5 | 6 | const {updateResult} = require('../../lib/updateResult'); 7 | 8 | describe('for no key', () => { 9 | const result = {}; 10 | const args = {}; 11 | 12 | const output = updateResult(args); 13 | 14 | test('should do nothing and return false', () => { 15 | expect(output).toBe(false); 16 | expect(result).toEqual({}); 17 | }); 18 | }); 19 | 20 | describe('when key is defined', () => { 21 | test('should update the result', () => { 22 | const result = {}; 23 | const args = { 24 | object:{ 25 | description: 'description', 26 | type: 'type', 27 | values: [1, 2], 28 | required: true, 29 | example: 'example', 30 | format: 'format' 31 | }, 32 | key: 'key', 33 | result: result 34 | }; 35 | 36 | updateResult(args); 37 | 38 | expect(result.key).toBeDefined(); 39 | 40 | const _result = result.key; 41 | 42 | expect(_result.description).toBe('description'); 43 | expect(_result.type).toBe('type'); 44 | expect(_result.values).toEqual([1, 2]); 45 | expect(_result.required).toBe(true); 46 | expect(_result.example).toBe('example'); 47 | expect(_result.format).toBe('format'); 48 | }); 49 | }); 50 | 51 | describe('when isRequired is set', () => { 52 | test('should use it', () => { 53 | const result = {}; 54 | const args = { 55 | object:{}, 56 | key: 'key', 57 | result: result, 58 | isRequired: true 59 | }; 60 | 61 | updateResult(args); 62 | 63 | expect(result.key).toBeDefined(); 64 | 65 | const _result = result.key; 66 | 67 | expect(_result.required).toBe(true); 68 | }); 69 | }); 70 | 71 | 72 | 73 | }); 74 | -------------------------------------------------------------------------------- /lib/nodejs/search/__tests__/build.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const build = require('../build'); 4 | const {mockLogger} = require('./mocks'); 5 | 6 | describe('search.build', () => { 7 | 8 | it('should throw an error when `ctx.pages` is undefined', () => { 9 | expect(() => { 10 | build({logger: mockLogger}); 11 | }).toThrow(new Error('Cannot find "pages" in the current context')); 12 | }); 13 | 14 | it('should create the expected search index and expected store', () => { 15 | const result = build({ 16 | logger: mockLogger, 17 | pages: [ 18 | {title: 'foo', content: '

Foo

', path: '/foo', source: 'foo.md'}, 19 | {title: 'bar', content: 'bar', path: '/bar', source: 'bar.md'} 20 | ] 21 | }); 22 | 23 | expect(Object.keys(result.store).length).toBe(3); 24 | }); 25 | 26 | 27 | it('should also use `ctx.pages.data` to support child process message', () => { 28 | const result = build({ 29 | logger: mockLogger, 30 | pages: { 31 | data: [ 32 | {title: 'foo', content: '

Foo

Body

Foo2

', path: '/foo', source: 'foo.md'}, 33 | {title: 'bar', content: 'bar', path: '/bar', source: 'bar.md'} 34 | ] 35 | } 36 | }); 37 | 38 | expect(Object.keys(result.store).length).toBe(4); 39 | }); 40 | 41 | it('should process "mardown" files and skip the others', () => { 42 | const result = build({ 43 | logger: mockLogger, 44 | pages: { 45 | data: [ 46 | {title: 'foo', content: 'foo', path: '/foo', source: 'foo.md'}, 47 | {title: 'bar', content: 'bar', path: '/bar', source: 'bar.markdown'}, 48 | {title: 'john', content: 'john', path: '/john', source: 'john.mdown'}, 49 | {title: 'exe', content: 'exe', path: '/exe', source: 'exe.exe'}, 50 | ] 51 | } 52 | }); 53 | 54 | expect(Object.keys(result.store).length).toBe(3); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/templates/securityDefinition.ejs: -------------------------------------------------------------------------------- 1 |
2 |

3 | <%= key %> 4 |

5 | <% if(definition.description){ %> 6 |
7 | <%= definition.description %> 8 |
9 | <% } %> 10 | <% if(definition.type){ %> 11 |
12 | The type of security scheme is <%= definition.type %>. 13 |
14 | <% } %> 15 | <% if(definition.name){ %> 16 |
17 | The name of the <% if(definition.in === 'header'){ %>header<% }else{ %>query parameter<% } %> to be used is <%= definition.name %>. 18 |
19 | <% } %> 20 | <% if(definition.flow){ %> 21 |
22 | The flow used by the OAuth2 security scheme is <%= definition.flow %>. 23 |
24 | <% } %> 25 | <% if(definition.authorizationUrl){ %> 26 |
27 | The authorization URL to be used for this flow is <%= definition.authorizationUrl %>. 28 |
29 | <% } %> 30 | <% if(definition.tokenUrl){ %> 31 |
32 | The token URL to be used for this flow is <%= definition.tokenUrl %>. 33 |
34 | <% } %> 35 | <% if(definition.scopes){ %> 36 |
37 | The available scopes for the OAuth2 security scheme are as follows. 38 |
39 | <% Object.keys(definition.scopes).forEach((key) => { %> 40 |
41 | <%- key %>: <%-definition.scopes[key]%> 42 |
43 | <% }) %> 44 |
45 |
46 | <% } %> 47 |
48 | -------------------------------------------------------------------------------- /source/style/_doc/vars.scss: -------------------------------------------------------------------------------- 1 | // breakpoints 2 | // ----------- 3 | $doc-breakpoint: 800px !default; 4 | $doc-breakpoint-small: 480px !default; 5 | 6 | // colors 7 | // ------------- 8 | $doc-color-primary: #fe6d05 !default; 9 | $doc-color-lightest: #fff !default; 10 | $doc-color-lighter: $dc-gray80 !default; 11 | $doc-color-light: #f3f3f3 !default; 12 | $doc-color-mediumlight: #eee !default; 13 | $doc-color-medium: #ccc !default; 14 | $doc-color-mediumdark: #999 !default; 15 | $doc-color-dark: #666 !default; 16 | $doc-color-darker: #444 !default; 17 | $doc-color-darkest: #333 !default; 18 | 19 | // navbar 20 | // ----- 21 | $doc-navbar-height: 50px !default; 22 | $doc-navbar-background-color: $doc-color-lightest !default; 23 | $doc-navbar-border-color: #e1e1e1 !default; 24 | $doc-navbar-logo-text-color: $doc-color-primary !default; 25 | $doc-navbar-width-collapsed: $doc-navbar-height !default; 26 | 27 | // sidebar 28 | // ------ 29 | $doc-sidebar-width: 230px !default; 30 | $doc-sidebar-width-collapsed: $doc-navbar-width-collapsed !default; 31 | $doc-sidebar-left-margin: 40px !default; 32 | $doc-sidebar-background-color: $doc-navbar-background-color !default; 33 | $doc-sidebar-color: $dc-gray30 !default; 34 | $doc-sidebar-label-color: $dc-gray20 !default; 35 | $doc-sidebar-current-color: $doc-color-primary !default; 36 | $doc-sidebar-item-border: 1px solid #ededed !default; 37 | $doc-sidebar-font-weight-normal: 300 !default; 38 | $doc-sidebar-font-weight-bold: 500 !default; 39 | $doc-sidebar-font-size-small: 12px !default; 40 | $doc-sidebar-font-size-normal: 14px !default; 41 | 42 | // support 43 | // ------- 44 | $doc-support-footer-border-color: $dc-gray60 !default; 45 | $doc-support-footer-color: $dc-gray50 !default; 46 | 47 | // formatting 48 | // ---------- 49 | 50 | // anchor 51 | $doc-anchor-color: $doc-color-medium !default; 52 | $doc-anchor-color-hover: $doc-color-primary !default; 53 | 54 | // blockquote 55 | $doc-blockquote-border-color: $doc-color-primary !default; 56 | 57 | // table 58 | $doc-table-border-color: $dc-gray60 !default; 59 | $doc-table-row-background-color: $dc-gray80 !default; 60 | 61 | // code 62 | $doc-inline-background-color: $dc-gray70 !default; 63 | -------------------------------------------------------------------------------- /lib/nodejs/search/searcher.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const escapeStringRegexp = require('escape-string-regexp'); 4 | 5 | module.exports = function searcher ({index, store}) { 6 | return function search (query) { 7 | let matches = index.search(query); // try search the exact keyword 8 | 9 | if (matches.length === 0) { // if no results, try with * "magic" 10 | matches = matches.concat(index.search(query + '*')); 11 | } 12 | 13 | return matches.reduce((results, match) => { 14 | // transform search match entries into actual results 15 | // by reconnecting them to store entry and enhance with useful properties 16 | // from search results 17 | if (store[match.ref]) { 18 | results.push(Object.assign({}, store[match.ref], { 19 | ref: match.ref, 20 | score: match.score, 21 | matchMetadata: match.matchData.metadata 22 | })); 23 | } 24 | return results; 25 | }, []).map((result) => { 26 | // enhance entry with highlight property representing 27 | // the actual matched tokens and the fields where those where found 28 | const highlight = Object.keys(result.matchMetadata).map((text) => { 29 | const fields = Object.keys(result.matchMetadata[text]); 30 | return { text, fields }; 31 | }); 32 | return Object.assign({}, result, { highlight }); 33 | 34 | }).map((entry) => { 35 | 36 | // truncate `body` where "center" is the first matched text to highlight 37 | if (entry.body && entry.highlight.length) { 38 | const text = entry.highlight[0].text; 39 | const firstHighlightIndex = entry.body.indexOf(text); 40 | entry.body = '...' + entry.body.substring(firstHighlightIndex - 100, firstHighlightIndex + 200) + '...'; 41 | } 42 | 43 | // add highlight markup 44 | entry.highlight.forEach((h) => { 45 | h.fields.forEach((f) => { 46 | if (f === 'body') { 47 | entry[f] = entry[f].replace(new RegExp(escapeStringRegexp(h.text), 'gi'), `${h.text}`); 48 | } 49 | }); 50 | }); 51 | return entry; 52 | }); 53 | }; 54 | }; 55 | -------------------------------------------------------------------------------- /lib/nodejs/search/generator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const { fork } = require('child_process'); 5 | const build = require('./build'); 6 | const hexoUtil = require('../hexo-util'); 7 | 8 | /** 9 | * Return a function that represents the hexo generator. 10 | * When "hexo server" command is used we fork a child process to produce the search index in background since the task can be a time expensive operation. 11 | * When the child process it's done, it sends back a message containing the computed search index 12 | * 13 | * @param options.hexo - the hexo global instance 14 | * @return {Function} - hexo generator 15 | */ 16 | module.exports = ({hexo}) => { 17 | const logger = hexo.log || console; 18 | const {url_for} = hexoUtil({hexo}); 19 | 20 | function setRoute ({ hexo, result }) { 21 | const route = hexo.config.theme_config.search.route; 22 | const url = url_for(route); 23 | logger.debug(`New search index available at: ${url}`); 24 | hexo.route.set(route, JSON.stringify(result)); 25 | } 26 | 27 | const {skip, background} = hexo.config.theme_config.search; 28 | 29 | if (skip) { return () => {}; } 30 | 31 | // build the index within the same process 32 | // wait for the task to complete 33 | if (!background) { 34 | return function (locals, cb) { 35 | logger.debug('Start building the search index'); 36 | const result = build({pages: locals.pages, rootPath: hexo.config.root, logger}); 37 | setRoute({hexo, result}); 38 | cb(); 39 | return result; 40 | }; 41 | } 42 | 43 | if (background) { 44 | logger.debug('Open a background child process to build the search index'); 45 | // use a child process to don't block the main process 46 | const child = fork(path.resolve(__dirname, './child.js')); 47 | child.on('message', (message) => { 48 | setRoute({ hexo, result: message }); 49 | }); 50 | 51 | return function (locals, cb) { 52 | logger.debug('Send a message to build the search index in background'); 53 | child.send({ 54 | pages: locals.pages, 55 | debug: hexo.env.debug, 56 | rootPath: hexo.config.root 57 | }); 58 | cb(); 59 | }; 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/__tests__/request.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const requestController = require('../request'); 4 | 5 | describe('controllers.request', () => { 6 | it('should transform the context as expected', () => { 7 | const headerParam = { 8 | name: 'headerParam', 9 | in: 'header', 10 | description: 'description', 11 | required: 'true', 12 | type: 'string', 13 | format: 'format', 14 | }; 15 | const pathParam = { 16 | name: 'pathParam', 17 | in: 'path', 18 | description: 'description', 19 | required: 'true', 20 | type: 'string', 21 | format: 'format', 22 | }; 23 | const queryParam = { 24 | name: 'queryParam', 25 | in: 'query', 26 | description: 'description', 27 | required: 'true', 28 | type: 'string', 29 | format: 'format', 30 | }; 31 | const formDataParam = { 32 | name: 'formDataParam', 33 | in: 'formData', 34 | description: 'description', 35 | required: 'true', 36 | type: 'string', 37 | format: 'format', 38 | }; 39 | const bodyParam = { 40 | name: 'bodyParam', 41 | in: 'body', 42 | description: 'description', 43 | required: 'true', 44 | type: 'string', 45 | format: 'format', 46 | }; 47 | const ctx = { 48 | operations: [ {} ], 49 | operation: { 50 | summary: 'A nice summary', 51 | description: 'A nice description', 52 | parameters: [ 53 | headerParam, 54 | pathParam, 55 | queryParam, 56 | formDataParam, 57 | bodyParam 58 | ], 59 | tags: [], 60 | produces: [], 61 | responses: {}, 62 | verb: 'get', 63 | path: '/path', 64 | title: 'title', 65 | } 66 | }; 67 | 68 | const {request} = requestController(ctx); 69 | 70 | expect(request).toBeInstanceOf(Object); 71 | expect(Object.keys(request).length).toBe(5); 72 | expect(request.header[0]).toBe(headerParam); 73 | expect(request.path[0]).toBe(pathParam); 74 | expect(request.query[0]).toBe(queryParam); 75 | expect(request.formData[0]).toBe(formDataParam); 76 | expect(request.body[0]).toBe(bodyParam); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/requestSample.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { highlight } = require('../../helpers'); 3 | 4 | const requestSample = (ctx) => { 5 | const sample = { 6 | header: {}, 7 | formData: {}, 8 | body: '' 9 | }; 10 | 11 | const request = ctx.request; 12 | const baseUrl = ctx.baseUrl; 13 | let path = ctx.path; 14 | const verb = ctx.verb.toUpperCase(); 15 | 16 | const pathParams = request.path; 17 | const queryParams = request.query; 18 | 19 | // Headers 20 | for (const header of request.header){ 21 | sample.header[header.name] = header['x-example']; 22 | } 23 | 24 | // Form data 25 | for (const formData of request.formData){ 26 | sample.formData[formData.name] = formData['x-example']; 27 | } 28 | 29 | // Update path with path params 30 | pathParams.forEach((param) => { 31 | if (param['x-example']){ 32 | path = path.replace('{' + param.name + '}', param['x-example']); 33 | } 34 | }); 35 | 36 | // Prepare query string 37 | const queryArray = []; 38 | queryParams.forEach((param) => { 39 | if (param['x-example']){ 40 | queryArray.push(param.name + '=' + param['x-example']); 41 | } 42 | }); 43 | const queryString = queryArray.join('&'); 44 | if (queryString){ 45 | path += '?' + queryString; 46 | } 47 | 48 | // Request body 49 | if (request.body.length){ 50 | const _body = request.body[0]['x-examples'] && request.body[0]['x-examples']['default']; 51 | sample.body = JSON.stringify(_body, null, 2); 52 | } 53 | 54 | 55 | // Create CURL string 56 | let curlString = 'curl -v -X ' + verb + ' ' + baseUrl.replace(/\/$/, '') + path + ' \\\n'; 57 | 58 | Object.keys(sample.header).forEach((header) => { 59 | if ('Authorization' === header){ 60 | curlString += '-H "Authorization: Bearer " \\\n'; 61 | } else { 62 | curlString += '-H "' + header + ': ' + sample.header[header] + '" \\\n'; 63 | } 64 | }); 65 | 66 | Object.keys(sample.formData).forEach((data) => { 67 | curlString += '-F "' + data + ': ' + sample.formData[data] + '" \\\n'; 68 | }); 69 | 70 | if (sample.body){ 71 | curlString += sample.body; 72 | } 73 | 74 | curlString = curlString.replace(/\\\n$/, ''); 75 | curlString = highlight({ 76 | code: curlString, 77 | lang: 'bash' 78 | }); 79 | 80 | return { 81 | curlString 82 | }; 83 | }; 84 | 85 | 86 | module.exports = requestSample; 87 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-ui/partials/v3/libs.ejs: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /lib/nodejs/search/__tests__/searcher.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const searcher = require('../searcher'); 4 | const build = require('../build'); 5 | const {mockLogger} = require('./mocks'); 6 | 7 | describe('search.searcher', () => { 8 | let search; 9 | 10 | beforeAll(() => { 11 | const {index, store} = build({ 12 | logger: mockLogger, 13 | pages: [ 14 | { 15 | title: 'sed-cursus', 16 | content: 'Sed cursus nisl a interdum cursus. Sed consequat mi sit amet nulla molestie, vel cursus urna maximus. Etiam ac est ut libero condimentum eleifend quis feugiat urna. Vivamus consectetur odio diam, at faucibus lacus auctor sed. Nunc laoreet tellus id congue lobortis. Etiam et convallis velit. Donec a mauris quis ligula dignissim feugiat non in urna. In augue turpis, varius nec lacus sit amet, dictum sodales ante. Morbi volutpat eget libero ut porttitor.', 17 | path: 'sed', 18 | source: 'sed.md' 19 | }, 20 | { 21 | title: 'lorem', 22 | content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed rhoncus elit libero, eget sollicitudin elit dignissim quis. Morbi ornare risus quis augue bibendum fringilla. Pellentesque pharetra pellentesque elit sed lobortis. Etiam dignissim, orci non convallis porta, erat nibh sodales nibh, et tempor leo diam ut lorem. Phasellus ullamcorper euismod dui, sit amet aliquam urna vestibulum accumsan. Integer ullamcorper cursus placerat. Aenean sodales, odio et efficitur mollis, sem nibh iaculis lacus, vel blandit nunc libero id risus. Ut ultrices diam ut magna porta, non molestie augue porttitor. Cras nec leo facilisis, lacinia risus quis, aliquet purus. In viverra et ligula eget ultrices. Integer ac tincidunt magna.', 23 | path: 'lorem', 24 | source: 'lorem.md' 25 | } 26 | ] 27 | }); 28 | search = searcher({ index, store }); 29 | }); 30 | 31 | it('should return a `search` function', () => { 32 | expect(typeof search).toBe('function'); 33 | }); 34 | 35 | describe('search', () => { 36 | it('should return expected results in the right order', () => { 37 | const entries = search('sed'); 38 | expect(entries.length).toBeGreaterThanOrEqual(2); 39 | expect(entries[0].ref).toBe('sed'); // has higher score since the search query was found also in the title 40 | expect(entries[1].ref).toBe('lorem'); 41 | }); 42 | 43 | it('should highlight and truncate the body according to the search query', () => { 44 | const entries = search('sed'); 45 | expect(entries[0].body.indexOf('sed') > - 1).toBe(true); 46 | }); 47 | }); 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/__tests__/_petstore.yaml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | host: petstore.swagger.io 8 | basePath: /v1 9 | schemes: 10 | - http 11 | consumes: 12 | - application/json 13 | produces: 14 | - application/json 15 | paths: 16 | /pets: 17 | get: 18 | summary: List all pets 19 | operationId: listPets 20 | tags: 21 | - pets 22 | parameters: 23 | - name: limit 24 | in: query 25 | description: How many items to return at one time (max 100) 26 | required: false 27 | type: integer 28 | format: int32 29 | responses: 30 | "200": 31 | description: An paged array of pets 32 | headers: 33 | x-next: 34 | type: string 35 | description: A link to the next page of responses 36 | schema: 37 | $ref: '#/definitions/Pets' 38 | default: 39 | description: unexpected error 40 | schema: 41 | $ref: '#/definitions/Error' 42 | post: 43 | summary: Create a pet 44 | operationId: createPets 45 | tags: 46 | - pets 47 | responses: 48 | "201": 49 | description: Null response 50 | default: 51 | description: unexpected error 52 | schema: 53 | $ref: '#/definitions/Error' 54 | /pets/{petId}: 55 | get: 56 | summary: Info for a specific pet 57 | operationId: showPetById 58 | tags: 59 | - pets 60 | parameters: 61 | - name: petId 62 | in: path 63 | required: true 64 | description: The id of the pet to retrieve 65 | type: string 66 | responses: 67 | "200": 68 | description: Expected response to a valid request 69 | schema: 70 | $ref: '#/definitions/Pets' 71 | default: 72 | description: unexpected error 73 | schema: 74 | $ref: '#/definitions/Error' 75 | definitions: 76 | Pet: 77 | required: 78 | - id 79 | - name 80 | properties: 81 | id: 82 | type: integer 83 | format: int64 84 | name: 85 | type: string 86 | tag: 87 | type: string 88 | Pets: 89 | type: array 90 | items: 91 | $ref: '#/definitions/Pet' 92 | Error: 93 | required: 94 | - code 95 | - message 96 | properties: 97 | code: 98 | type: integer 99 | format: int32 100 | message: 101 | type: string 102 | -------------------------------------------------------------------------------- /lib/nodejs/project-partial/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | function createHelper ({theme_config, source_dir, render, log}) { 6 | 7 | let partials = null; 8 | 9 | function projectPartialHelper (area) { 10 | 11 | // Get and normalize "partials" configuration once and cache the result 12 | // 13 | // We do this operation inside the helper function because: 14 | // 15 | // - it means that `project_partial` template helper is really used in the layout 16 | // - the rendering process is involved (it will not be excuted if you run `hexo clean`, for example) 17 | 18 | if (!partials) { 19 | partials = getPartials({ 20 | theme_config, source_dir, render, log 21 | }); 22 | } 23 | 24 | if (!partials[area]) { return ''; } 25 | 26 | // in a hexo helper plugin function, 27 | // "this" represents the same context that hexo pass 28 | // when rendering theme's layout partials 29 | const locals = this; 30 | return partials[area] 31 | .map((p) => renderPartial(p, locals)) 32 | .filter(h => h) 33 | .join('\n'); 34 | } 35 | 36 | function renderPartial (partialPath, locals) { 37 | try { 38 | return render.renderSync({ path: partialPath }, locals); 39 | } catch (err) { 40 | log.error(`There was a problem while rendering partial "${partialPath}". skip rendering.`); 41 | log.error(`${err.message}`); 42 | log.debug(err); 43 | return ''; 44 | } 45 | } 46 | 47 | return projectPartialHelper; 48 | } 49 | 50 | function getPartials ({theme_config, source_dir, render, log}) { 51 | const partials = Object.assign({ 52 | head_start: [], 53 | head_end: [], 54 | footer_start: [], 55 | footer_end: [] 56 | }, theme_config.partials); 57 | 58 | const isValidPartial = (p) => { 59 | return typeof p === 'string'; 60 | }; 61 | 62 | const isRenderablePartial = (p) => { 63 | if (!render.isRenderable(p) || render.getOutput(p) !== 'html') { 64 | log.warn(`partial "${p}" cannot be rendered or the output is not html.`); 65 | return false; 66 | } 67 | return true; 68 | }; 69 | 70 | // "normalize" partials for each area: 71 | // - always use array type for each area 72 | // - exclude invalid partials or non renderable partials 73 | return Object.keys(partials).reduce((normalizedPartials, area) => { 74 | 75 | if (!Array.isArray(partials[area])) { 76 | partials[area] = [partials[area]]; 77 | } 78 | 79 | normalizedPartials[area] = partials[area] 80 | .filter(isValidPartial) 81 | .map((p) => path.resolve(source_dir, p)) 82 | .filter(isRenderablePartial); 83 | 84 | return normalizedPartials; 85 | 86 | }, {}); 87 | } 88 | 89 | module.exports = { getPartials, createHelper }; 90 | -------------------------------------------------------------------------------- /plugins/swagger-to-html.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const { Promise } = require('bluebird'); 6 | const validUrl = require('valid-url'); 7 | 8 | module.exports = ({hexo}) => { 9 | 10 | const { url_for } = require('../lib/nodejs/hexo-util')({hexo}); 11 | const {getRoute, getDigest, prepareRoute} = require('../lib/nodejs/swagger-store')({hexo}); 12 | 13 | class SwaggerProcessor{ 14 | 15 | /* 16 | * Default templating enging is 'html'. 17 | */ 18 | 19 | constructor (engine = 'html'){ 20 | const availableEngines = ['md', 'html']; 21 | this.engine = null; 22 | 23 | if (availableEngines.includes(engine)){ 24 | this.engine = engine; 25 | } else { 26 | throw new TypeError(`Templating Engine(${engine}) is not supported.`); 27 | } 28 | } 29 | 30 | handleDownload (specPath){ 31 | const downloadRoute = prepareRoute(specPath); 32 | const hexoRoute = url_for(downloadRoute); 33 | return hexoRoute; 34 | } 35 | 36 | get processor (){ 37 | 38 | const transformer = require('../lib/nodejs/swagger-to-html')({hexo}); 39 | const engine = this.engine; 40 | const that = this; 41 | 42 | return function (args){ 43 | const ctx = this; 44 | let specPath = args[0]; 45 | 46 | if(!validUrl.isUri(specPath)){ 47 | specPath = path.resolve(path.dirname(ctx.full_source), specPath); 48 | } 49 | 50 | let output = ''; 51 | const transformerPromise = new Promise((resolve, reject) => { 52 | const readableStream = transformer(specPath); 53 | 54 | readableStream.on('readable', () => { 55 | let chunk; 56 | while ((chunk = readableStream.read()) !== null) { 57 | output += chunk; 58 | } 59 | }) 60 | .on('end', () => { 61 | resolve(output); 62 | }) 63 | .on('error', (err) => { 64 | reject(err); 65 | }); 66 | }); 67 | 68 | 69 | return transformerPromise.then((output) => { 70 | const downloadRoute = that.handleDownload(specPath); 71 | return hexo.render.render({text: output.toString(), engine: engine }) 72 | .then((html) => 73 | `
74 |
75 | Download Schema 76 |
77 | ${html} 78 |
` 79 | ); 80 | }); 81 | }; 82 | } 83 | } 84 | 85 | hexo.extend.tag.register('swagger_to_html', new SwaggerProcessor('html').processor, {async: true}); 86 | }; 87 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const controllers = require('./controllers'); 5 | 6 | 7 | module.exports = { 8 | head: { 9 | order: 0, 10 | template: path.resolve(__dirname, './templates/head.ejs') 11 | }, 12 | operations: { 13 | order: 2, 14 | template: path.resolve(__dirname, './templates/operations.ejs'), 15 | controller: controllers.operations 16 | }, 17 | operation: { 18 | include: true, 19 | template: path.resolve(__dirname, './templates/operation.ejs'), 20 | controller: controllers.operation 21 | }, 22 | header: { 23 | include: true, 24 | template: path.resolve(__dirname, './templates/header.ejs') 25 | }, 26 | requestParams: { 27 | include: true, 28 | template: path.resolve(__dirname, './templates/requestParams.ejs') 29 | }, 30 | requestBody: { 31 | include: true, 32 | template: path.resolve(__dirname, './templates/requestBody.ejs'), 33 | controller: controllers.requestBody 34 | }, 35 | request: { 36 | include: true, 37 | template: path.resolve(__dirname, './templates/request.ejs'), 38 | controller: controllers.request 39 | }, 40 | responses: { 41 | include: true, 42 | template: path.resolve(__dirname, './templates/responses.ejs'), 43 | controller: controllers.responses 44 | }, 45 | response: { 46 | include: true, 47 | template: path.resolve(__dirname, './templates/response.ejs'), 48 | controller: controllers.response 49 | }, 50 | emptyResponse: { 51 | include: true, 52 | template: path.resolve(__dirname, './templates/emptyResponse.ejs') 53 | }, 54 | responseBody: { 55 | include: true, 56 | template: path.resolve(__dirname, './templates/responseBody.ejs') 57 | }, 58 | requestSample: { 59 | include: true, 60 | template: path.resolve(__dirname, './templates/requestSample.ejs'), 61 | controller: controllers.requestSample 62 | }, 63 | responseSample: { 64 | include: true, 65 | template: path.resolve(__dirname, './templates/responseSample.ejs'), 66 | controller: controllers.responseSample 67 | }, 68 | security: { 69 | order: 1, 70 | template: path.resolve(__dirname, './templates/security.ejs'), 71 | controller: controllers.security 72 | }, 73 | securityDefinition: { 74 | include: true, 75 | template: path.resolve(__dirname, './templates/securityDefinition.ejs') 76 | }, 77 | securityRequirement: { 78 | include: true, 79 | template: path.resolve(__dirname, './templates/securityRequirement.ejs') 80 | }, 81 | requestContent: { 82 | include: true, 83 | template: path.resolve(__dirname, './templates/requestContent.ejs') 84 | }, 85 | formData: { 86 | include: true, 87 | template: path.resolve(__dirname, './templates/formData.ejs') 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /lib/browser/utils.js: -------------------------------------------------------------------------------- 1 | const urljoin = require('url-join'); 2 | const $ = require('jquery'); 3 | 4 | /** 5 | * Creates a function that mimic 6 | * url_for hexo helper function 7 | * 8 | * @param {Object} props - configuration properties 9 | * @return {Function} url_for function 10 | */ 11 | function url_for (props) { 12 | return function (path) { 13 | if (/^(f|ht)tps?:\/\//i.test(path)) { 14 | return path; 15 | } 16 | const url = urljoin( 17 | props.config.root, 18 | path 19 | ); 20 | return url.replace(/\/{2,}/g, '/'); // removes double slashes 21 | }; 22 | } 23 | 24 | /** 25 | * Get heading html nodes in the current page 26 | * @return {jQuery} - a jQuery object 27 | */ 28 | function getTOCHeaders () { 29 | return $('h2'); 30 | } 31 | 32 | /** 33 | * dispatch an event 34 | * @param {string} eventType 35 | * @param {Object} [payload={}] - payload of the event 36 | * @return void 37 | */ 38 | function dispatch (eventType, payload = {}) { 39 | const evt = new CustomEvent(eventType, { detail: payload }); 40 | window.dispatchEvent(evt); 41 | } 42 | 43 | /** 44 | * subscribe to a specific event 45 | * 46 | * @param {string} eventType 47 | * @param {Function} cb - function called when the event is dispatched 48 | * @return {Function} - unsusscribe function 49 | */ 50 | function subscribeOn (eventType, cb) { 51 | const handler = (e) => { 52 | cb(Object.assign({}, {type: e.type}, e.detail)); 53 | }; 54 | 55 | window.addEventListener(eventType, handler); 56 | 57 | return () => { 58 | window.removeEventListener(eventType, handler); 59 | }; 60 | } 61 | 62 | /** 63 | * Reduce a map of [string]:boolean into a string, 64 | * concatenating the string keys when boolean value is true 65 | * 66 | * @exmaple 67 | * 68 | * // Button Component 69 | * 70 | * function Button(props) { 71 | * const className = classNames({ 72 | * 'btn': true, 73 | * 'btn-disabled': props.disabled, 74 | * [props.classNames]: true // append classNames passed as props if any 75 | * }); 76 | * 77 | * return ( 78 | * 79 | * ) 80 | * } 81 | * 82 | * // Usage 83 | * 84 | * 92 | * 93 | * @param {Object} [map={}] 94 | * @return {string} 95 | */ 96 | function classNames (map = {}) { 97 | return Object.keys(map).reduce((acc, key) => { 98 | if (typeof key !== 'string' || key === 'undefined') { return acc; } 99 | if (map[key]) { 100 | return acc.concat(key); 101 | } 102 | return acc; 103 | }, []).join(' '); 104 | } 105 | 106 | 107 | module.exports = {url_for, getTOCHeaders, dispatch, subscribeOn, classNames}; 108 | -------------------------------------------------------------------------------- /lib/browser/search/components.jsx: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const {SHOW_SEARCH_RESULTS, HIDE_SEARCH_RESULTS} = require('./actions'); 3 | const {dispatch} = require('../utils'); 4 | 5 | class SearchForm extends React.Component { 6 | 7 | constructor (props) { 8 | super(props); 9 | } 10 | 11 | handleKeyUp (e) { 12 | const query = (e.target.value || '').trim(); 13 | 14 | if (!query) { 15 | dispatch(HIDE_SEARCH_RESULTS); 16 | return; 17 | } 18 | 19 | if (query.length < 3) { return; } 20 | 21 | const results = this.props.search(query); 22 | 23 | dispatch(SHOW_SEARCH_RESULTS, {results, query}); 24 | 25 | if (typeof this.props.onSearch === 'function') { 26 | this.props.onSearch(); 27 | } 28 | } 29 | 30 | render () { 31 | 32 | if (!this.props.search) { return null; } 33 | 34 | return ( 35 |
36 | 41 | 44 |
45 | ); 46 | } 47 | } 48 | 49 | function SearchResultsTitle ({results, query}) { 50 | return ( 51 |
52 |

53 | { results.length ? results.length : 'No' } results for "{query}" 54 |

55 | 56 | { !results.length ?

There are no results for "{query}". Why not try typing another keyword?

: null } 57 |
58 | ); 59 | } 60 | 61 | function SearchResultsList ({results}) { 62 | if (!results.length) { 63 | return null; 64 | } 65 | 66 | const handleSearchResultLinkClick = () => dispatch(HIDE_SEARCH_RESULTS); 67 | 68 | const createMarkup = (html) => ({ __html: html }); 69 | 70 | return ( 71 |
    72 | { results.map((result, i) => { 73 | return ( 74 |
  • 75 | 79 | {result.title} 80 | 81 | | 82 | score: {result.score.toFixed(2)} 83 |

    84 |
  • 85 | ); 86 | })} 87 |
88 | ); 89 | } 90 | 91 | module.exports = {SearchForm, SearchResultsTitle, SearchResultsList}; 92 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at oss-review@zalando.de. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /lib/nodejs/search/__tests__/generator.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | jest.mock('../../hexo-util', () => mockHexoUtil); 4 | 5 | const mockSend = jest.fn(); 6 | const mockFork = jest.fn() 7 | .mockImplementation(() => ({ 8 | on: () => {}, 9 | send: mockSend 10 | })); 11 | 12 | jest.mock('child_process', () => ({ 13 | fork: mockFork 14 | })); 15 | 16 | const {mockCb, mockHexo, mockHexoUtil} = require('./mocks'); 17 | const generator = require('../generator'); 18 | 19 | describe('search.generator', () => { 20 | let generate; 21 | 22 | beforeAll(() => { 23 | generate = generator({ 24 | hexo: mockHexo 25 | }); 26 | }); 27 | 28 | it('should return a generate function', () => { 29 | expect(typeof generate).toBe('function'); 30 | }); 31 | 32 | describe('generate', () => { 33 | let result; 34 | beforeAll(() => { 35 | result = generate({ 36 | pages: [ 37 | {title: 'foo', content: '', path: '/foo', source: 'foo.md'}, 38 | {title: 'bar', content: 'bar', path: '/bar', source: 'bar.md'} 39 | ] 40 | }, mockCb); 41 | }); 42 | 43 | it('should return search `index` and `store` from pages', () => { 44 | expect(result.index).not.toBeUndefined(); 45 | expect(result.store).not.toBeUndefined(); 46 | }); 47 | 48 | describe('index.search', () => { 49 | it('should return the expected results', () => { 50 | const results = result.index.search('b*'); 51 | expect(results.length).toBeGreaterThan(0); 52 | }); 53 | }); 54 | 55 | it('store should contain only one specific entry, when "search:only" is used', () => { 56 | const result = generate({ 57 | pages: [ 58 | {title: 'foo', content: '', path: '/foo', source: 'foo.md'}, 59 | {title: 'bar', content: 'bar', path: '/bar', source: 'bar.md', search: 'only'} 60 | ] 61 | }, () => {}); 62 | 63 | expect(Object.keys(result.store).length).toBe(1); 64 | expect(result.store['/bar']).toBeDefined(); 65 | }); 66 | 67 | it('should throw an error when "pages" is not an array', () => { 68 | let error; 69 | try { 70 | generate({ pages: null }, () => {}); 71 | } catch (err) { 72 | error = err; 73 | } 74 | expect(error instanceof Error).toBe(true); 75 | }); 76 | }); 77 | 78 | describe('when skip is true', () => { 79 | beforeAll(() => { 80 | mockHexo.config.theme_config.search = { 81 | skip: true 82 | }; 83 | generate = generator({ 84 | hexo: mockHexo 85 | }); 86 | }); 87 | 88 | test('should return an empty function', () => { 89 | const result = generate(); 90 | expect(result).toBeUndefined(); 91 | }); 92 | }); 93 | 94 | describe('when background is true', () => { 95 | beforeAll(() => { 96 | mockHexo.config.theme_config.search = { 97 | background: true 98 | }; 99 | }); 100 | 101 | test('should run task in background', () => { 102 | generate = generator({ 103 | hexo: mockHexo 104 | }); 105 | 106 | generate({pages: 'testPages'}, () => {}); 107 | expect(mockSend).toHaveBeenCalled(); 108 | expect(mockFork).toHaveBeenCalled(); 109 | }); 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /lib/nodejs/__tests__/swagger-store.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | /** 5 | * Mocking crypto.createHash('md5').update('...').digest('hex') 6 | */ 7 | const mockCrypto = { 8 | createHash: jest.fn(() => { 9 | return mockCrypto; 10 | }), 11 | update: jest.fn(() => { 12 | return mockCrypto; 13 | }), 14 | digest: jest.fn(() => { 15 | return 'DIGEST'; 16 | }) 17 | }; 18 | 19 | jest.mock('crypto',() => mockCrypto); 20 | 21 | function mockSwagger (){ 22 | this.swaggerObject = ''; 23 | 24 | this.validate = () => { 25 | this.swaggerObject += '-VALIDATED'; 26 | return Promise.resolve(this); 27 | }; 28 | 29 | this.merge = () => { 30 | this.swaggerObject += '-MERGED'; 31 | return Promise.resolve(this); 32 | }; 33 | 34 | this.unmerge = () => { 35 | this.swaggerObject += '-UNMERGED'; 36 | return Promise.resolve(this); 37 | }; 38 | 39 | this.decorate = mockSwagger.prototype.decorate = (x) => { 40 | this.swaggerObject += '-' + x(); 41 | return Promise.resolve(this); 42 | }; 43 | } 44 | 45 | jest.mock('../swagger', () => ({ 46 | Swagger: mockSwagger, 47 | decorators: { 48 | docExclude: () => 'DOC_EXCLUDE' , 49 | host: () => 'HOST' 50 | } 51 | })); 52 | 53 | describe('swagger-store', () => { 54 | const mockHexo = { 55 | config: { 56 | theme_config: {} 57 | } 58 | }; 59 | const swaggerStore = require('../swagger-store')({hexo: mockHexo}); 60 | 61 | it('should return swagger.helpers', () => { 62 | expect(Object.keys(swaggerStore).length).toBe(5); 63 | }); 64 | 65 | it('should get digest', () => { 66 | const digest = swaggerStore.getDigest('/path/to/file'); 67 | expect(digest).toEqual('DIGEST'); 68 | expect(mockCrypto.createHash).toHaveBeenCalled(); 69 | expect(mockCrypto.createHash).toHaveBeenCalledWith('md5'); 70 | expect(mockCrypto.update).toHaveBeenCalled(); 71 | expect(mockCrypto.update).toHaveBeenCalledWith('/path/to/file'); 72 | expect(mockCrypto.digest).toHaveBeenCalled(); 73 | expect(mockCrypto.digest).toHaveBeenCalledWith('hex'); 74 | }); 75 | 76 | it('should prepare routes', () => { 77 | const downloadRoute = swaggerStore.prepareRoute('/path/to/file.yaml'); 78 | expect(downloadRoute).toBe('DIGEST/file.yaml'); 79 | }); 80 | 81 | it('should set and get route', () => { 82 | const result = swaggerStore.setRoute('ROUTE', 'DATA'); 83 | const routeData = swaggerStore.getRoutes('ROUTE'); 84 | expect(result).toBe(true); 85 | expect(routeData).toBe('DATA'); 86 | }); 87 | 88 | describe('when no route is passed in setRoute', () => { 89 | it('should no do anything', () => { 90 | const result = swaggerStore.setRoute(); 91 | expect(result).toBe(false); 92 | }); 93 | }); 94 | 95 | describe('when no route is passed in getRoute', () => { 96 | it('should return all routes', () => { 97 | const routes = swaggerStore.getRoutes(); 98 | expect(routes).toEqual({'ROUTE': 'DATA'}); 99 | }); 100 | }); 101 | 102 | it('should get swagger', () => { 103 | return swaggerStore 104 | .getSwagger('/path/to/swagger') 105 | .then((swagger) => { 106 | expect(swagger.downloadRoute).toBe('DIGEST/swagger'); 107 | expect(swagger.swagger.swaggerObject).toBe('-MERGED-DOC_EXCLUDE-HOST-UNMERGED'); 108 | }); 109 | }); 110 | }); 111 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/__tests__/lib/processItems/object.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockTraverse = jest.fn(); 4 | const mockUpdateResult = jest.fn(); 5 | 6 | 7 | jest.mock('../../../lib/traverse', () => ({ 8 | traverse: mockTraverse 9 | })); 10 | 11 | jest.mock('../../../lib/updateResult', () => ({ 12 | updateResult: mockUpdateResult 13 | })); 14 | 15 | const processObject = require('../../../lib/processItems/object'); 16 | 17 | describe('swagger-parser.lib.processItems.object', () => { 18 | describe('when object is blank', () => { 19 | test('should use default values', () => { 20 | const object = {}; 21 | const key = 'key'; 22 | const isRequired = true; 23 | const result = {}; 24 | 25 | processObject({object, key, isRequired, result}); 26 | 27 | expect(mockTraverse).toHaveBeenCalledTimes(0); 28 | 29 | }); 30 | }); 31 | 32 | describe('when there is no key', () => { 33 | test('should use propertyName as key', () => { 34 | const object = { 35 | properties: { 36 | foo: { 37 | type: 'string' 38 | } 39 | }, 40 | required: ['foo'] 41 | }; 42 | const key = ''; 43 | const isRequired = true; 44 | const result = {}; 45 | 46 | processObject({object, key, isRequired, result}); 47 | 48 | expect(mockUpdateResult).toHaveBeenCalled(); 49 | expect(mockUpdateResult).toHaveBeenCalledWith({object, key, isRequired, result}); 50 | 51 | expect(mockTraverse).toHaveBeenCalledTimes(1); 52 | expect(mockTraverse).toHaveBeenCalledWith({object: {type: 'string'} , key: 'foo', isRequired: true}); 53 | 54 | }); 55 | }); 56 | 57 | describe('if object have additionalProperties', () => { 58 | test('should handle them', () => { 59 | const object = { 60 | additionalProperties: { 61 | properties: { 62 | foo: { 63 | type: 'string' 64 | } 65 | }, 66 | required: ['foo'] 67 | } 68 | }; 69 | const key = ''; 70 | const isRequired = true; 71 | const result = {}; 72 | 73 | processObject({object, key, isRequired, result}); 74 | 75 | expect(mockUpdateResult).toHaveBeenCalled(); 76 | expect(mockUpdateResult).toHaveBeenCalledWith({object, key, isRequired, result}); 77 | 78 | expect(mockTraverse).toHaveBeenCalledTimes(1); 79 | expect(mockTraverse).toHaveBeenCalledWith({object: {type: 'string'} , key: 'foo', isRequired: true}); 80 | 81 | 82 | }); 83 | }); 84 | 85 | test('should process object', () => { 86 | const object = { 87 | properties: { 88 | foo: { 89 | type: 'string' 90 | }, 91 | bar: { 92 | type: 'object' 93 | } 94 | }, 95 | required: ['foo'] 96 | }; 97 | const key = 'key'; 98 | const isRequired = true; 99 | const result = {}; 100 | 101 | processObject({object, key, isRequired, result}); 102 | 103 | expect(mockUpdateResult).toHaveBeenCalled(); 104 | expect(mockUpdateResult).toHaveBeenCalledWith({object, key, isRequired, result}); 105 | 106 | expect(mockTraverse).toHaveBeenCalledTimes(2); 107 | expect(mockTraverse).toHaveBeenCalledWith({object: {type: 'string'} , key: key + '.foo', isRequired: true}); 108 | expect(mockTraverse).toHaveBeenCalledWith({object: {type: 'object'} , key: key + '.bar', isRequired: false}); 109 | }); 110 | }); 111 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-store.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {Swagger, decorators } = require('./swagger'); 4 | const crypto = require('crypto'); 5 | const path = require('path'); 6 | const routeMap = Object.create(null); 7 | const validUrl = require('valid-url'); 8 | const parseTemplateObject = require('@rbarilani/parse-template-object'); 9 | 10 | module.exports = ({hexo}) => { 11 | 12 | let swaggerParserOptions = Object.assign({}, (hexo.config.theme_config.swagger_parser || {})); 13 | swaggerParserOptions = parseTemplateObject(swaggerParserOptions, { 14 | imports: { 15 | env: process.env 16 | } 17 | }); 18 | 19 | const getDigest = (swaggerPath) => { 20 | return crypto 21 | .createHash('md5') 22 | .update(swaggerPath) 23 | .digest('hex'); 24 | }; 25 | 26 | const prepareRoute = function (specPath){ 27 | const digest = getDigest(specPath); 28 | const basename = path.basename(specPath); 29 | const downloadRoute = digest + '/' + basename; 30 | return downloadRoute; 31 | }; 32 | 33 | 34 | const handleRoute = (swagger, swaggerPath) => { 35 | const jsonSchema = swagger.swaggerJson; 36 | const yamlSchema = swagger.swaggerYaml; 37 | const extname = path.extname(swaggerPath); 38 | const downloadRoute = prepareRoute(swaggerPath); 39 | 40 | if (extname.match(/^\.json/)){ 41 | setRoute(downloadRoute, jsonSchema); 42 | setRoute(downloadRoute.replace(/\.json/, '.yaml'), yamlSchema); 43 | } else { 44 | setRoute(downloadRoute, yamlSchema); 45 | setRoute(downloadRoute.replace(/\.yaml/, '.json'), jsonSchema); 46 | } 47 | 48 | /** 49 | * Hexo converts all the yaml file to json file. 50 | * removeLocalYamlRoute updates routeMap to remove that route. 51 | */ 52 | if (!validUrl.isUri(swaggerPath)){ 53 | removeLocalYamlRoute(swaggerPath); 54 | } 55 | return Promise.resolve(downloadRoute); 56 | }; 57 | 58 | 59 | const removeLocalYamlRoute = (swaggerPath) => { 60 | const sourceDir = hexo.source_dir; 61 | const relativePath = swaggerPath 62 | .replace(sourceDir, '') 63 | .replace(/\.yaml$/, '.json') 64 | .replace(/\.yml$/, '.json'); 65 | setRoute(relativePath, null); 66 | }; 67 | 68 | 69 | const getSwagger = function (swaggerPath, dereferenced = false){ 70 | const swagger = new Swagger(swaggerPath, swaggerParserOptions); 71 | 72 | return swagger 73 | .merge() 74 | .then(swagger => swagger.decorate(decorators.docExclude)) 75 | .then(swagger => swagger.decorate(decorators.host)) 76 | .then(swagger => swagger.unmerge(dereferenced)) 77 | .then(swagger => { 78 | return handleRoute(swagger, swaggerPath); 79 | }) 80 | .then(downloadRoute => { 81 | const result = { 82 | swagger, 83 | downloadRoute 84 | }; 85 | return result; 86 | }); 87 | }; 88 | 89 | const setRoute = function (route, data){ 90 | if (!route){ 91 | return false; 92 | } 93 | routeMap[route] = data; 94 | return true; 95 | }; 96 | 97 | 98 | const getRoutes = function (route){ 99 | let routes = null; 100 | 101 | if (route){ 102 | routes = routeMap[route]; 103 | } else { 104 | routes = routeMap; 105 | } 106 | return routes; 107 | }; 108 | 109 | 110 | return { 111 | getSwagger, 112 | getRoutes, 113 | setRoute, 114 | getDigest, 115 | prepareRoute 116 | }; 117 | 118 | }; 119 | -------------------------------------------------------------------------------- /lib/nodejs/swagger/__tests__/swagger-mocks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const referencedSchema = { 4 | 'swagger': '2.0', 5 | 'parameters': { 6 | 'Auth': { 7 | 'name': 'Auth' 8 | } 9 | }, 10 | 'paths': { 11 | '/pets': { 12 | 'get': { 13 | 'parameters': [ 14 | { 15 | '$ref': '#/parameters/Auth' 16 | }, 17 | { 18 | 'name': 'limit', 19 | 'in': 'query', 20 | 'description': 'Maximum number of results to return', 21 | 'required': false, 22 | 'type': 'integer', 23 | 'format': 'int32', 24 | 'x-example': 10 25 | } 26 | ], 27 | 'responses': { 28 | 'default': { 29 | 'schema': { 30 | '$ref': '#/definitions/Response' 31 | } 32 | } 33 | } 34 | } 35 | } 36 | }, 37 | 'definitions': { 38 | 'Response': { 39 | 'type': 'object', 40 | 'properties': { 41 | 'name': { 42 | 'type': 'string' 43 | } 44 | } 45 | } 46 | } 47 | }; 48 | 49 | const dereferencedSchema = { 50 | 'swagger': '2.0', 51 | 'parameters': { 52 | 'Auth': { 53 | 'name': 'Auth' 54 | } 55 | }, 56 | 'paths': { 57 | '/pets': { 58 | 'get': { 59 | 'parameters': [ 60 | { 61 | 'name': 'Auth' 62 | }, 63 | { 64 | 'name': 'limit', 65 | 'in': 'query', 66 | 'description': 'Maximum number of results to return', 67 | 'required': false, 68 | 'type': 'integer', 69 | 'format': 'int32', 70 | 'x-example': 10 71 | } 72 | ], 73 | 'responses': { 74 | 'default': { 75 | 'schema': { 76 | 'type': 'object', 77 | 'properties': { 78 | 'name': { 79 | 'type': 'string' 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | }, 88 | 'definitions': { 89 | 'Response': { 90 | 'type': 'object', 91 | 'properties': { 92 | 'name': { 93 | 'type': 'string' 94 | } 95 | } 96 | } 97 | } 98 | }; 99 | 100 | const mergedSchema = { 101 | 'swagger': '2.0', 102 | 'parameters': { 103 | 'Auth': { 104 | 'name': 'Auth' 105 | } 106 | }, 107 | 'paths': { 108 | '/pets': { 109 | 'get': { 110 | 'parameters': [ 111 | { 112 | '$ref': '#/parameters/Auth', 113 | 'name': 'Auth' 114 | }, 115 | { 116 | 'name': 'limit', 117 | 'in': 'query', 118 | 'description': 'Maximum number of results to return', 119 | 'required': false, 120 | 'type': 'integer', 121 | 'format': 'int32', 122 | 'x-example': 10 123 | } 124 | ], 125 | 'responses': { 126 | 'default': { 127 | 'schema': { 128 | '$ref': '#/definitions/Response', 129 | 'type': 'object', 130 | 'properties': { 131 | 'name': { 132 | 'type': 'string' 133 | } 134 | } 135 | } 136 | } 137 | } 138 | } 139 | } 140 | }, 141 | 'definitions': { 142 | 'Response': { 143 | 'type': 'object', 144 | 'properties': { 145 | 'name': { 146 | 'type': 'string' 147 | } 148 | } 149 | } 150 | } 151 | }; 152 | 153 | module.exports = { 154 | referencedSchema, 155 | dereferencedSchema, 156 | mergedSchema 157 | }; 158 | -------------------------------------------------------------------------------- /lib/nodejs/project-partial/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {getPartials, createHelper} = require('../index'); 4 | const {merge} = require('lodash'); 5 | 6 | const mockLogger = { 7 | debug: jest.fn(), 8 | info () {}, 9 | warn () {}, 10 | error: jest.fn() 11 | }; 12 | 13 | describe('project-partial.createHelper', () => { 14 | const defaultScenario = { 15 | source_dir: '/root', 16 | log: mockLogger, 17 | render: { 18 | isRenderable: () => true, 19 | getOutput: () => 'html', 20 | renderSync: jest.fn().mockReturnValue('foo') 21 | }, 22 | theme_config: { 23 | partials: { 24 | head_start: './foo.ejs' 25 | } 26 | }, 27 | area: 'head_start' 28 | }; 29 | 30 | const scenarios = [{ 31 | message: 'should render one partial for head_start area', 32 | expected: { 33 | helper_output: 'foo' 34 | } 35 | }, 36 | { 37 | message: 'should render multiple partials for head_start area', 38 | theme_config: { 39 | partials: { 40 | head_start: ['./foo.ejs', './foo.ejs', './foo.ejs'] 41 | } 42 | }, 43 | expected: { 44 | helper_output: 'foo\nfoo\nfoo' 45 | } 46 | }, { 47 | message: 'should render empty string because error occured while rendering', 48 | render: { 49 | renderSync: () => { throw new Error('foo'); } 50 | }, 51 | expected: { 52 | error: true, 53 | helper_output: '' 54 | } 55 | },{ 56 | message: 'should render empty string because area doesn\'t exist', 57 | area: 'foo_bar', 58 | expected: { 59 | helper_output: '' 60 | } 61 | }]; 62 | 63 | scenarios.map((scenario) => { 64 | const s = merge({}, defaultScenario, scenario); 65 | s.log.error.mockClear(); 66 | const helper = createHelper(s); 67 | 68 | it(s.message, () => { 69 | expect(s.log.error).not.toHaveBeenCalled(); 70 | expect(typeof helper).toEqual('function'); 71 | expect(helper(s.area)).toEqual(s.expected.helper_output); 72 | expect(helper(s.area)).toEqual(s.expected.helper_output); 73 | 74 | if (s.expected.error) { 75 | expect(s.log.error).toHaveBeenCalled(); 76 | } 77 | }); 78 | }); 79 | }); 80 | 81 | describe('project-partial.getPartials', () => { 82 | const defaultScenario = { 83 | source_dir: '/root', 84 | log: mockLogger, 85 | }; 86 | 87 | const scenarios = [{ 88 | render: { 89 | isRenderable: () => true, 90 | getOutput: () => 'html' 91 | }, 92 | theme_config: { 93 | partials: { 94 | head_start: './foo.ejs' 95 | } 96 | }, 97 | expected: { 98 | head_start: ['/root/foo.ejs'] 99 | } 100 | }, 101 | { 102 | render: { 103 | isRenderable: () => true, 104 | getOutput: () => 'jpeg' 105 | }, 106 | theme_config: { 107 | partials: { 108 | head_start: './foo.ejs' 109 | } 110 | }, 111 | expected: { 112 | head_start: [] 113 | } 114 | }]; 115 | 116 | scenarios.map((scenario) => { 117 | const s = Object.assign({}, defaultScenario, scenario); 118 | const partials = getPartials(s); 119 | 120 | it('should return array for every area', () => { 121 | Object.keys(partials).forEach((area) => { 122 | const p = partials[area]; 123 | expect(Array.isArray(p)).toBe(true); 124 | }); 125 | }); 126 | 127 | it('should transform a value into an array with one element', () => { 128 | expect(partials.head_start.length).toEqual(s.expected.head_start.length); 129 | }); 130 | 131 | it('should resolve partials\' path relative to project source dir', () => { 132 | expect(partials.head_start).toEqual(s.expected.head_start); 133 | }); 134 | }); 135 | }); 136 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hexo-theme-doc", 3 | "version": "1.0.0-rc.1", 4 | "main": "index.js", 5 | "description": "A documentation theme for Hexo", 6 | "keywords": [ 7 | "hexo", 8 | "documentation", 9 | "theme", 10 | "swagger", 11 | "open-api" 12 | ], 13 | "license": "MIT", 14 | "homepage": "https://github.com/zalando-incubator/hexo-theme-doc", 15 | "author": "Bhaskar Melkani ", 16 | "contributors": [ 17 | "Ruben Barilani " 18 | ], 19 | "scripts": { 20 | "test": "jest --config ./jest.json", 21 | "test:coverage": "NODE_ENV=test jest --coverage --no-cache --runInBand --config ./jest.json", 22 | "lint": "eslint lib packages scripts --ext .js,.jsx", 23 | "lint:report": "npm run lint -- -f checkstyle --output-file target/checkstyle.xml", 24 | "compile:sass": "npm run compile:sass:doc && npm run compile:sass:swagger-v2 && npm run compile:sass:swagger-v3", 25 | "compile:sass:doc": "node-sass _doc.scss source/style/doc.css --include-path node_modules --output-style='compressed'", 26 | "compile:sass:swagger-v2": "node-sass source/style/_swagger/swagger-ui-v2.scss source/style/swagger-ui-v2.css --output-style='compressed'", 27 | "compile:sass:swagger-v3": "node-sass source/style/_swagger/swagger-ui-v3.scss source/style/swagger-ui-v3.css --output-style='compressed'", 28 | "compile:js": "webpack -p", 29 | "compile": "npm run compile:sass && npm run compile:js && node banner.js", 30 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", 31 | "prerelease": "npm run lint && npm run test && npm run compile && npm run changelog", 32 | "watch:sass": "nodemon -e scss -x 'npm run compile:sass'" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git@github.com:zalando-incubator/hexo-theme-doc.git" 37 | }, 38 | "dependencies": { 39 | "@rbarilani/parse-template-object": "^1.0.0", 40 | "babel-preset-es2015": "^6.24.1", 41 | "babel-preset-react": "^6.24.1", 42 | "babel-register": "^6.26.0", 43 | "cheerio": "^1.0.0-rc.1", 44 | "clipboard": "^1.7.1", 45 | "codacy-coverage": "^2.0.3", 46 | "consolidate": "^0.14.5", 47 | "dress-code": "~2.4.0", 48 | "ejs": "^2.5.7", 49 | "escape-string-regexp": "^1.0.5", 50 | "hexo-log": "^0.2.0", 51 | "is-plain-obj": "^1.1.0", 52 | "jquery": "^3.2.1", 53 | "js-crawler": "^0.3.19", 54 | "js-yaml": "^3.8.4", 55 | "jsx-loader": "^0.13.2", 56 | "lodash": "^4.17.4", 57 | "lunr": "2.1.0", 58 | "node-jsx": "^0.13.3", 59 | "react": "^16.0.0", 60 | "react-dom": "^16.0.0", 61 | "smooth-scroll": "^12.1.5", 62 | "strip-indent": "^2.0.0", 63 | "striptags": "^3.0.1", 64 | "swagger-parser": "^4.0.0", 65 | "touch": "^3.1.0", 66 | "url-join": "^2.0.2", 67 | "valid-url": "^1.0.9" 68 | }, 69 | "devDependencies": { 70 | "babel-loader": "^7.1.2", 71 | "bootstrap-sass": "3.3.7", 72 | "conventional-changelog-cli": "^1.3.3", 73 | "enzyme": "^3.1.0", 74 | "enzyme-adapter-react-16": "^1.0.2", 75 | "eslint": "^4.1.1", 76 | "eslint-plugin-jest": "^20.0.3", 77 | "eslint-plugin-react": "^7.2.1", 78 | "hexo": "^3.3.7", 79 | "hexo-renderer-ejs": "^0.3.0", 80 | "jest": "^20.0.4", 81 | "jest-junit": "^2.1.0", 82 | "node-sass": "^4.5.3", 83 | "nodemon": "^1.12.1", 84 | "promise-polyfill": "^6.0.2", 85 | "raf": "^3.4.0", 86 | "react-test-renderer": "^16.0.0", 87 | "swagger-ui-dist": "^3.16.0", 88 | "webpack": "^3.0.0", 89 | "whatwg-fetch": "^2.0.3" 90 | }, 91 | "jest-junit": { 92 | "output": "./target/junit.xml" 93 | }, 94 | "babel": { 95 | "presets": [ 96 | "react", 97 | "es2015" 98 | ] 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/modules/controllers/__tests__/requestSample.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const mockHighlight = jest.fn(() => 'CURL_STRING'); 5 | 6 | jest.mock('../../../helpers', () => ({ 7 | highlight: mockHighlight 8 | })); 9 | 10 | const requestSampleController = require('../requestSample'); 11 | 12 | describe('controllers.requestSample', () => { 13 | it('should transform the context as expected', () => { 14 | const requestBody = { 15 | 'data': { 16 | 'type': 'Order', 17 | 'id': '123' 18 | } 19 | }; 20 | 21 | const ctx = { 22 | 'request': { 23 | 'header': [ 24 | { 25 | 'name': 'Authorization', 26 | 'in': 'header', 27 | 'description': 'JWT token', 28 | 'required': true, 29 | 'type': 'string', 30 | 'format': 'JWT token', 31 | 'x-example': 'ThisIsAnAuthToken' 32 | }, 33 | { 34 | 'name': 'X-Flow-Id', 35 | 'in': 'header', 36 | 'description': 'For troubleshooting', 37 | 'required': false, 38 | 'type': 'string', 39 | 'format': 'uuid', 40 | 'x-example': 'ThisIsXFLOWID' 41 | } 42 | ], 43 | 'path': [ 44 | { 45 | 'name': 'mid', 46 | 'in': 'path', 47 | 'description': 'To identify merchant this operation is carried out for', 48 | 'required': true, 49 | 'type': 'string', 50 | 'format': 'uuid', 51 | 'x-example': 'ThisIsMerchantId' 52 | }, 53 | { 54 | 'name': 'oid', 55 | 'in': 'path', 56 | 'description': 'The ID of the order for which the items are being requested.', 57 | 'required': true, 58 | 'type': 'string', 59 | 'format': 'uuid', 60 | 'x-example': 'ThisIsAnOrderId' 61 | } 62 | ], 63 | 'query': [ 64 | { 65 | 'name': 'include', 66 | 'in': 'query', 67 | 'description': 'Comma seperated field names to be included', 68 | 'required': false, 69 | 'type': 'string', 70 | 'x-example': 'ThisIsInclude' 71 | } 72 | ], 73 | 'formData': [ 74 | { 75 | 'name': 'file', 76 | 'in': 'formData', 77 | 'description': "The merchant's document to create", 78 | 'required': true, 79 | 'type': 'file', 80 | 'x-example': 'ThisIsInclude' 81 | }, 82 | { 83 | 'name': 'document-type', 84 | 'in': 'formData', 85 | 'description': "The merchant's document to create", 86 | 'required': true, 87 | 'type': 'string', 88 | 'x-example': 'ThisIsInclude' 89 | } 90 | ], 91 | 'body': [{ 92 | 'in': 'body', 93 | 'name': 'order', 94 | 'description': 'Patch of order', 95 | 'required': true, 96 | 'schema': {}, 97 | 'x-examples': { 98 | 'default': requestBody 99 | } 100 | }] 101 | }, 102 | 'path': '/some/path/{mid}/{oid}', 103 | 'verb': 'get', 104 | 'baseUrl': 'https://example.com/' 105 | } ; 106 | 107 | let curlStr = 'curl -v -X GET https://example.com/some/path/ThisIsMerchantId/ThisIsAnOrderId?include=ThisIsInclude \\\n-H "Authorization: Bearer " \\\n-H "X-Flow-Id: ThisIsXFLOWID" \\\n-F "file: ThisIsInclude" \\\n-F "document-type: ThisIsInclude" \\\n'; 108 | 109 | curlStr += JSON.stringify(requestBody, null, 2); 110 | 111 | const highlightArgs = { 112 | code: curlStr, 113 | lang: 'bash' 114 | }; 115 | 116 | const {curlString} = requestSampleController(ctx); 117 | 118 | expect(mockHighlight).toHaveBeenCalled(); 119 | 120 | expect(mockHighlight).toHaveBeenCalledWith(highlightArgs); 121 | 122 | expect(curlString).toBe('CURL_STRING'); 123 | 124 | }); 125 | }); 126 | 127 | -------------------------------------------------------------------------------- /lib/browser/search/__tests__/components.test.jsx: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const {shallow, mount} = require('enzyme'); 3 | 4 | const {HIDE_SEARCH_RESULTS, SHOW_SEARCH_RESULTS} = require('../actions'); 5 | 6 | describe('browser.search.components', () => { 7 | 8 | const mockDispatch = jest.fn(); 9 | 10 | jest.mock('../../utils', () => ({ dispatch: mockDispatch })); 11 | 12 | afterEach(() => { 13 | mockDispatch.mockClear(); 14 | }); 15 | 16 | describe('SearchForm', () => { 17 | const {SearchForm} = require('../components.jsx'); 18 | 19 | it('should not render anything if search props is null', () => { 20 | const searchForm = shallow(); 21 | expect(searchForm.getElement()).toEqual(null); 22 | }); 23 | 24 | it('should render the input if search props is a function', () => { 25 | const searchForm = shallow( {}} />); 26 | expect(searchForm.find('input').length).toEqual(1); 27 | }); 28 | 29 | it('should hide search results if search query is empty', () => { 30 | const searchForm = shallow( {}} />); 31 | searchForm.find('input').simulate('keyup', { target: { value: '' } } ); 32 | expect(mockDispatch).toHaveBeenCalledWith(HIDE_SEARCH_RESULTS); 33 | }); 34 | 35 | it('should return immediately if query is too short', () => { 36 | const searchForm = shallow( []} />); 37 | searchForm.find('input').simulate('keyup', { target: { value: 'fo' } } ); 38 | expect(mockDispatch).not.toHaveBeenCalled(); 39 | }); 40 | 41 | it('should show results if search query is not empty or too short', () => { 42 | const searchForm = shallow( []} />); 43 | searchForm.find('input').simulate('keyup', { target: { value: 'foobar' } } ); 44 | expect(mockDispatch).toHaveBeenCalledWith(SHOW_SEARCH_RESULTS, expect.any(Object)); 45 | }); 46 | 47 | 48 | it('should call onSearch callback if it\'s a function', () => { 49 | const mockOnSearch = jest.fn(); 50 | const searchForm = shallow( []} onSearch={mockOnSearch} />); 51 | searchForm.find('input').simulate('keyup', { target: { value: 'foobar' } } ); 52 | expect(mockOnSearch).toHaveBeenCalled(); 53 | }); 54 | }); 55 | 56 | describe('SearchResultsTitle', () => { 57 | const {SearchResultsTitle} = require('../components.jsx'); 58 | it('should shallow render without any error', () => { 59 | const title = shallow(); 60 | expect(title.length).toEqual(1); 61 | }); 62 | }); 63 | 64 | describe('SearchResultsList', () => { 65 | const {SearchResultsList} = require('../components.jsx'); 66 | 67 | it('should not render anything if no results', () => { 68 | const list = shallow(); 69 | expect(list.getElement()).toEqual(null); 70 | }); 71 | 72 | it('should show the expected number of results', () => { 73 | const results = [{ 74 | score: 0.4, 75 | title: 'foobar', 76 | body: 'foobar' 77 | }]; 78 | const list = shallow(); 79 | expect(list.find('ul').children('li').length).toBe(1); 80 | }); 81 | 82 | it('should render result.body html value', () => { 83 | const results = [{ 84 | score: 0.4, 85 | title: 'foobar', 86 | body: '
foobar
' 87 | }]; 88 | const list = mount(); 89 | expect(list.find('ul').children('li').find('p').html()).toBe('

foobar

'); 90 | }); 91 | 92 | it('should hide results when a link is clicked', () => { 93 | const results = [{ 94 | score: 0.4, 95 | title: 'foobar', 96 | body: 'foobar' 97 | }]; 98 | const list = shallow(); 99 | 100 | list.find('ul').children('li').find('a').last().simulate('click'); 101 | 102 | expect(mockDispatch).toHaveBeenCalledWith(HIDE_SEARCH_RESULTS); 103 | }); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /lib/nodejs/search/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const lunr = require('lunr'); 4 | const striptags = require('striptags'); 5 | const cheerio = require('cheerio'); 6 | const path = require('path'); 7 | 8 | /** 9 | * Build lunr search index and store 10 | * 11 | * Iterates over "registered" Indexers to get documents 12 | * to add to the search index and a store 13 | * 14 | * @param ctx - context 15 | * @return {{index, store}} 16 | */ 17 | module.exports = function build (ctx) { 18 | const logger = ctx.logger; 19 | const hrstart = process.hrtime(); 20 | const INDEXERS = [PagesIndexer]; 21 | 22 | const { documents, store, items } = INDEXERS.reduce((acc, indexer) => { 23 | const { documents, store, items } = indexer.build(ctx); 24 | acc.store = Object.assign({}, acc.store, store); 25 | acc.documents = acc.documents.concat(documents); 26 | acc.items = acc.items.concat(items); 27 | return acc; 28 | }, { 29 | documents: [], // all documents that should be added to search index 30 | items: [], // all processed items 31 | store: {} // merged store 32 | }); 33 | 34 | logger.debug(`Building search index and store for ${items.length} items, mapping to ${documents.length} documents`); 35 | 36 | const index = lunr(function () { 37 | this.ref('id'); 38 | this.field('title'); 39 | this.field('body'); 40 | documents.forEach(function (doc) { 41 | this.add(doc); 42 | }, this); 43 | }); 44 | const hrend = process.hrtime(hrstart); 45 | logger.debug(`Search index was built in ${hrend[0]}s, ${(hrend[1] / 1000000).toFixed()}ms`); 46 | 47 | return { 48 | index, 49 | store, 50 | 51 | }; 52 | }; 53 | 54 | function sanitize (html) { 55 | return striptags(html, [], ' ').trim() // strip html tags 56 | .replace(new RegExp('\n', 'g'), '') /* removes linebreaks */ // eslint-disable-line no-control-regex 57 | .replace(/ +(?= )/g,'') // removes double spaces 58 | .replace(/{|"|}|<|>/g, ''); 59 | } 60 | 61 | function urlFor (rootPath, pagePath) { 62 | return path.join(rootPath || '/', pagePath); 63 | } 64 | 65 | 66 | /** 67 | * PagesIndexer 68 | * 69 | * @private 70 | * @type {{filter: Function, toData: Function}} 71 | */ 72 | const PagesIndexer = { 73 | build: (ctx) => { 74 | const pages = Array.isArray(ctx.pages) ? ctx.pages : (ctx.pages && ctx.pages.data); 75 | const store = {}; 76 | 77 | if (!Array.isArray(pages)) { 78 | throw new Error('Cannot find "pages" in the current context'); 79 | } 80 | 81 | const items = pages.filter((page) => { 82 | const ext = page.source.split('.').pop().toLowerCase(); 83 | return page.path 84 | && page.content 85 | && page.search !== 'exclude' 86 | && (ext === 'md' || ext === 'markdown' || ext === 'mdown'); 87 | }); 88 | 89 | const only = items.find((page) => { 90 | return page.search === 'only'; 91 | }); 92 | 93 | if (only) { 94 | items.length = 0; 95 | items.push(only); 96 | } 97 | 98 | const documents = items.reduce((acc, page) => { 99 | acc.push({ 100 | id: page.path, 101 | title: page.title, 102 | body: sanitize(page.content), 103 | path: urlFor(ctx.rootPath, page.path) 104 | }); 105 | 106 | const $ = cheerio.load(page.content); 107 | $('h1,h2').each(function () { 108 | const elem = $(this); 109 | const anchorPagePath = `${page.path}#${elem.attr('id')}`; 110 | 111 | let body = []; 112 | 113 | elem.nextUntil('h2').each(function () { 114 | body.push($(this).html()); 115 | }); 116 | 117 | body = body.join(' '); 118 | 119 | acc.push({ 120 | id: `${page.path}#${elem.attr('id')}`, 121 | title: sanitize(elem.html()), 122 | body: sanitize(body), 123 | path: urlFor(ctx.rootPath, anchorPagePath) 124 | }); 125 | }); 126 | return acc; 127 | }, []); 128 | 129 | documents.forEach((doc) => { 130 | store[doc.id] = { 131 | title: doc.title, 132 | path: doc.path, 133 | body: doc.body 134 | }; 135 | }); 136 | return { documents, store, items }; 137 | } 138 | }; 139 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/helpers/parser/__tests__/lib/traverse.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockProcessArray = jest.fn(() => {}); 4 | const mockProcessObject = jest.fn(() => {}); 5 | const mockProcessEnum = jest.fn(() => {}); 6 | const mockProcessDefault = jest.fn(() => {}); 7 | 8 | jest.mock('../../lib/processItems', () => ({ 9 | processArray: mockProcessArray, 10 | processObject: mockProcessObject, 11 | processEnum: mockProcessEnum, 12 | processDefault: mockProcessDefault 13 | })); 14 | 15 | const {traverse} = require('../../lib/traverse'); 16 | 17 | describe('swagger-parser.lib.traverse', () => { 18 | describe('if type is array', () => { 19 | const args = { 20 | object: { 21 | type: 'array' 22 | }, 23 | key: 'mockKey', 24 | isRequired: 'isRequired' 25 | }; 26 | 27 | 28 | test('should call processArray', () => { 29 | 30 | traverse(args); 31 | 32 | args['result'] = undefined; 33 | 34 | expect(mockProcessArray).toHaveBeenCalled(); 35 | expect(mockProcessArray).toHaveBeenCalledWith(args); 36 | }); 37 | }); 38 | 39 | describe('if type is object', () => { 40 | const args = { 41 | object: { 42 | type: 'object' 43 | }, 44 | key: 'mockKey', 45 | isRequired: 'isRequired' 46 | }; 47 | 48 | 49 | test('should call processObject', () => { 50 | 51 | traverse(args); 52 | 53 | args['result'] = undefined; 54 | 55 | expect(mockProcessObject).toHaveBeenCalled(); 56 | expect(mockProcessObject).toHaveBeenCalledWith(args); 57 | }); 58 | }); 59 | 60 | describe('if type is enum', () => { 61 | const args = { 62 | object: { 63 | enum: [] 64 | }, 65 | key: 'mockKey', 66 | isRequired: 'isRequired' 67 | }; 68 | 69 | 70 | test('should call processEnum', () => { 71 | 72 | traverse(args); 73 | 74 | args['result'] = undefined; 75 | 76 | expect(mockProcessEnum).toHaveBeenCalled(); 77 | expect(mockProcessEnum).toHaveBeenCalledWith(args); 78 | }); 79 | }); 80 | 81 | describe('for default case', () => { 82 | const args = { 83 | object: { 84 | type: 'string' 85 | }, 86 | key: 'mockKey', 87 | isRequired: 'isRequired' 88 | }; 89 | 90 | 91 | test('should call processEnum', () => { 92 | 93 | traverse(args); 94 | 95 | args['result'] = undefined; 96 | 97 | expect(mockProcessDefault).toHaveBeenCalled(); 98 | expect(mockProcessDefault).toHaveBeenCalledWith(args); 99 | }); 100 | }); 101 | 102 | describe('when init is true', () => { 103 | const args = { 104 | object: { 105 | type: 'string' 106 | }, 107 | key: 'mockKey', 108 | isRequired: 'isRequired', 109 | init: true 110 | }; 111 | 112 | 113 | test('should set result to {}', () => { 114 | 115 | traverse(args); 116 | 117 | args['result'] = {}; 118 | 119 | delete args['init']; 120 | 121 | expect(mockProcessDefault).toHaveBeenCalled(); 122 | expect(mockProcessDefault).toHaveBeenCalledWith(args); 123 | }); 124 | }); 125 | 126 | describe('when allOf is present', () => { 127 | const args = { 128 | object: { 129 | type: 'object', 130 | allOf: [ 131 | { 132 | type: 'object', 133 | properties: { 134 | name: { 135 | type: 'string' 136 | } 137 | } 138 | }, 139 | { 140 | required: ['id'], 141 | properties: { 142 | id: { 143 | type: 'integer', 144 | format: 'int64' 145 | } 146 | } 147 | } 148 | ] 149 | }, 150 | key: 'mockKey', 151 | isRequired: 'isRequired', 152 | init: true 153 | }; 154 | 155 | 156 | test('should parse allOf', () => { 157 | 158 | traverse(args); 159 | 160 | const expectedArgs = { 161 | object: { 162 | type: 'object', 163 | required: ['id'], 164 | properties: { 165 | name: { 166 | type: 'string' 167 | }, 168 | id: { 169 | type: 'integer', 170 | format: 'int64' 171 | } 172 | } 173 | }, 174 | key: 'mockKey', 175 | isRequired: 'isRequired', 176 | result: {} 177 | }; 178 | 179 | expect(mockProcessObject).toHaveBeenCalled(); 180 | expect(mockProcessObject).toHaveBeenCalledWith(expectedArgs); 181 | }); 182 | }); 183 | }); 184 | -------------------------------------------------------------------------------- /lib/browser/navigation/__tests__/components.test.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const {mount, shallow} = require('enzyme'); 3 | 4 | const itShouldAppendClasses = (Component) => { 5 | it('should append classes if any', () => { 6 | const className = 'custom-class'; 7 | const component = shallow(React.createFactory(Component)({ 8 | className 9 | })); 10 | expect(component.hasClass(className)).toBe(true); 11 | }); 12 | }; 13 | 14 | describe('browser.navigation.components', () => { 15 | 16 | describe('Navbar', () => { 17 | const {Navbar} = require('../components.jsx'); 18 | it('should wrap children', () => { 19 | const navbar = mount( 20 | 21 |
22 |
23 | ); 24 | expect(navbar.find('#foobar').length).toBe(1); 25 | }); 26 | }); 27 | 28 | describe('Logo', () => { 29 | const {Logo} = require('../components.jsx'); 30 | it('should not render anything if props.navigation.logo is not set', () => { 31 | const logo = shallow(); 32 | expect(logo.getElement()).toEqual(null); 33 | }); 34 | 35 | it('should render if props.navigation.logo is set', () => { 36 | const logo = shallow( {}} navigation={{logo: {}}} />); 37 | expect(logo.find('a').length).toBe(1); 38 | }); 39 | }); 40 | 41 | describe('Sidebar', () => { 42 | const {Sidebar, SidebarItem} = require('../components.jsx'); 43 | it('should render the expected number of items', () => { 44 | const sidebar = shallow(); 48 | expect(sidebar.find(SidebarItem).length).toEqual(2); 49 | }); 50 | 51 | it('should render the expected number of items when no items', () => { 52 | const sidebar = shallow(); 53 | expect(sidebar.find(SidebarItem).length).toEqual(0); 54 | }); 55 | 56 | it('should render the expected number of items when support navigation feature is on', () => { 57 | const support = { 58 | navigation: true, 59 | navigation_label: 'FOO', 60 | link_url: '/foo', 61 | link_text: 'foo' 62 | }; 63 | 64 | const sidebar = shallow( 65 | 69 | ); 70 | expect(sidebar.find(SidebarItem).length).toEqual(4); 71 | }); 72 | }); 73 | 74 | describe('SidebarItem', () => { 75 | const {SidebarItem} = require('../components.jsx'); 76 | 77 | const createComponent = (props = {}) => { 78 | return React.createFactory(SidebarItem)(Object.assign({ 79 | item: { 80 | path: '/bar', 81 | type: 'link' 82 | }, 83 | page: { 84 | path: '/foo' 85 | }, 86 | url_for: () => {}, 87 | }, props)); 88 | }; 89 | 90 | it('shouldn\'t add TOC items if the item doesn\'t represent the current page', () => { 91 | const sidebarItem = mount(createComponent()); 92 | expect(sidebarItem.find('.doc-sidebar-list__toc-list').length).toBe(0); 93 | }); 94 | 95 | it('should add TOC items if the item represents the current page', () => { 96 | const sidebarItem = mount(createComponent({ 97 | item: { 98 | path: '/foo', 99 | type: 'link', 100 | isCurrent: true 101 | }, 102 | page: { 103 | path: '/foo' 104 | } 105 | })); 106 | expect(sidebarItem.find('.doc-sidebar-list__toc-list').length).toBe(1); 107 | }); 108 | 109 | it('should render a label if the item type is "label"', () => { 110 | const sidebarItem = mount(createComponent({ 111 | item: { 112 | type: 'label' 113 | } 114 | })); 115 | expect(sidebarItem.find('.doc-sidebar-list__item--label').length).toBe(1); 116 | }); 117 | }); 118 | 119 | describe('SidebarToggle', () => { 120 | const {SidebarToggle} = require('../components.jsx'); 121 | 122 | it('should shallow render without any error', () => { 123 | const toggle = shallow(); 124 | expect(toggle.length).toEqual(1); 125 | }); 126 | 127 | itShouldAppendClasses(SidebarToggle); 128 | }); 129 | 130 | describe('SidebarClose', () => { 131 | const {SidebarClose} = require('../components.jsx'); 132 | 133 | it('should shallow render without any error', () => { 134 | const close = shallow(); 135 | expect(close.length).toEqual(1); 136 | }); 137 | 138 | itShouldAppendClasses(SidebarClose); 139 | }); 140 | }); 141 | -------------------------------------------------------------------------------- /source/style/_doc/swagger-to-html.scss: -------------------------------------------------------------------------------- 1 | .doc-swagger-to-html{ 2 | 3 | .inline-code{ 4 | background: #f5f7fa; 5 | padding: 1px 5px; 6 | border-radius: 2px; 7 | border: 1px solid #e4e4e4; 8 | } 9 | 10 | .download-btn{ 11 | overflow: hidden; 12 | 13 | &__link{ 14 | float: right; 15 | font-weight: initial; 16 | } 17 | } 18 | 19 | .head{ 20 | 21 | margin: 15px 0; 22 | overflow: hidden; 23 | $head-height: 34px; 24 | 25 | &__title{ 26 | overflow: hidden; 27 | 28 | &__text{ 29 | float: left; 30 | color: #4a4a4a; 31 | margin-top: 0px; 32 | margin-bottom: 5px; 33 | line-height: $head-height; 34 | } 35 | &__version{ 36 | float: right; 37 | line-height: $head-height; 38 | margin-top: 0px; 39 | } 40 | } 41 | 42 | &__description{ 43 | float: left; 44 | margin-top: 50px; 45 | } 46 | } 47 | 48 | .operation-info{ 49 | 50 | &__title{ 51 | border-bottom: 2px solid #d8d8d8; 52 | padding-bottom: 10px; 53 | position: relative; 54 | } 55 | 56 | &__verb{ 57 | overflow: hidden; 58 | margin-top: 30px; 59 | } 60 | 61 | &__verb__name{ 62 | font-size: 14px; 63 | font-weight: bold; 64 | float: left; 65 | color: #f7f7f7; 66 | padding: 10px 15px; 67 | 68 | &--get{ 69 | $verb-color: #1fb3cb; 70 | background: $verb-color; 71 | border: 1px solid $verb-color; 72 | } 73 | 74 | &--post{ 75 | $verb-color: #65cb1c; 76 | background: $verb-color; 77 | border: 1px solid $verb-color; 78 | } 79 | 80 | &--patch{ 81 | $verb-color: #bb5cba; 82 | background: $verb-color; 83 | border: 1px solid $verb-color; 84 | } 85 | 86 | &--delete{ 87 | $verb-color: #cc3a1d; 88 | background: $verb-color; 89 | border: 1px solid $verb-color; 90 | } 91 | 92 | &--put{ 93 | $verb-color: #cc711d; 94 | background: $verb-color; 95 | border: 1px solid $verb-color; 96 | } 97 | } 98 | 99 | &__verb__path{ 100 | font-size: 16px; 101 | color: #646464; 102 | padding: 10px 5px 10px 20px; 103 | border: solid 1px #d1d1d1; 104 | border-left: none; 105 | overflow: hidden; 106 | } 107 | } 108 | 109 | .request, .response{ 110 | overflow: hidden; 111 | 112 | &__title{ 113 | overflow: hidden; 114 | 115 | &__sample, &__reference{ 116 | float: left; 117 | width: 50%; 118 | } 119 | } 120 | 121 | &__content{ 122 | border: solid 1px #ededed; 123 | width: 100%; 124 | table-layout: fixed; 125 | 126 | &__sample, &__reference{ 127 | width: 50%; 128 | padding: 15px; 129 | vertical-align: top; 130 | } 131 | 132 | &__sample{ 133 | background: #ededed; 134 | } 135 | } 136 | } 137 | 138 | .reference-card{ 139 | 140 | margin-top: 40px; 141 | 142 | &:first-of-type{ 143 | margin-top: initial; 144 | } 145 | 146 | &__title{ 147 | border-bottom: 2px solid #ededed; 148 | padding-bottom: 10px; 149 | color: #4a4a4a; 150 | } 151 | 152 | &__content{ 153 | margin-top: 15px; 154 | 155 | &__info{ 156 | overflow: hidden; 157 | 158 | &__name, &__type, &__required{ 159 | float: left; 160 | border-left: 2px solid #ededed; 161 | font-size: 14px; 162 | padding: 0 15px; 163 | 164 | &:first-child{ 165 | border-left: initial; 166 | } 167 | } 168 | 169 | &__name{ 170 | font-weight: bold; 171 | font-size: 14px; 172 | padding-left: initial; 173 | } 174 | 175 | &__type{ 176 | color: #b6b6b6; 177 | } 178 | 179 | &__required{ 180 | color: #ff917d; 181 | } 182 | } 183 | 184 | &__description, &__example, &__usage, &__values{ 185 | margin-top: 10px; 186 | } 187 | } 188 | 189 | } 190 | 191 | .sample-snippet{ 192 | 193 | &__copy-btn{ 194 | margin-top: 10px; 195 | 196 | &.dc--has-tooltip:after{ 197 | text-transform: initial; 198 | } 199 | } 200 | 201 | .highlight{ 202 | margin: 0; 203 | overflow: hidden; 204 | 205 | table{ 206 | table-layout: fixed; 207 | width: 100%; 208 | 209 | .code{ 210 | pre{ 211 | background: initial; 212 | padding: initial; 213 | } 214 | 215 | .line{ 216 | white-space: pre-wrap; 217 | } 218 | } 219 | } 220 | } 221 | } 222 | 223 | } 224 | -------------------------------------------------------------------------------- /lib/nodejs/swagger-to-html/__tests__/core.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const {createTransformer} = require('../core'); 5 | 6 | describe('swagger-processor.core', () => { 7 | 8 | describe('createTransformer', () => { 9 | const testTranformer = createTransformer({ 10 | modules: { 11 | body: { 12 | order: 1, 13 | template: path.resolve(__dirname, './templates/body.ejs'), 14 | controller: (ctx) => { 15 | return { text: ctx.body_text }; 16 | } 17 | }, 18 | head: { 19 | order: 0, 20 | template: path.resolve(__dirname, './templates/head.ejs') 21 | } 22 | } 23 | }); 24 | 25 | it('should be a function', () => { 26 | expect(typeof testTranformer).toEqual('function'); 27 | }); 28 | 29 | it('should transform the input in the expected output', (done) => { 30 | const data = []; 31 | testTranformer({ 32 | title: 'title', 33 | body_text: 'body' 34 | }).on('data', (chunk) => { 35 | data.push(chunk.toString()); 36 | }).on('error', done.fail) 37 | .on('end', () => { 38 | expect(data[0].trim()).toEqual('title'); 39 | expect(data[1].trim()).toEqual('body'); 40 | done(); 41 | }); 42 | }); 43 | 44 | it('should transform the input in the expected output in the right order', (done) => { 45 | const delayedTransformer = createTransformer({ 46 | render: (tpl) => { 47 | // "inject" a custom render function to emulate a delay 48 | const delay = tpl === 'a' ? 500 : 0; 49 | return new Promise((resolve) => { 50 | setTimeout(() => { 51 | resolve(tpl); 52 | }, delay); 53 | }); 54 | }, 55 | modules: { 56 | b: { 57 | order: 1, 58 | template: 'b' 59 | }, 60 | a: { 61 | order: 0, 62 | template: 'a' 63 | } 64 | } 65 | }); 66 | 67 | const data = []; 68 | delayedTransformer().on('data', (chunk) => { 69 | data.push(chunk.toString()); 70 | }).on('error', done.fail) 71 | .on('end', () => { 72 | expect(data[0].trim()).toEqual('a'); 73 | expect(data[1].trim()).toEqual('b'); 74 | done(); 75 | }); 76 | }); 77 | 78 | it('should emit error and close the stream when input function rejects', (done) => { 79 | const testError = new Error('test error'); 80 | const failingTransformer = createTransformer({ 81 | input: () => Promise.reject(testError), 82 | modules: {} 83 | }); 84 | failingTransformer().on('error', (err) => { 85 | expect(err).toBe(testError); 86 | done(); 87 | }); 88 | }); 89 | 90 | it('should emit error and close the stream when controller function throw an error', (done) => { 91 | const testError = new Error('test error'); 92 | const failingTransformer = createTransformer({ 93 | modules: { 94 | foo: { 95 | controller: () => { throw testError; } 96 | } 97 | } 98 | }); 99 | failingTransformer().on('error', (err) => { 100 | expect(err).toBe(testError); 101 | done(); 102 | }); 103 | }); 104 | }); 105 | 106 | describe('include_module', () => { 107 | const includeModuleTestTransformer = createTransformer({ 108 | modules: { 109 | head: { 110 | template: path.resolve(__dirname, './templates/head-include-module.ejs') 111 | }, 112 | subtitle: { 113 | include: true, 114 | template: path.resolve(__dirname, './templates/includes/subtitle.ejs') 115 | }, 116 | description: { 117 | include: true, 118 | template: path.resolve(__dirname, './templates/includes/description.ejs') 119 | } 120 | } 121 | }); 122 | 123 | it('should transform the input using include_module template helper', (done) => { 124 | const data = []; 125 | includeModuleTestTransformer({ 126 | title: 'title', 127 | subtitle: 'subtitle' 128 | }).on('data', (chunk) => { 129 | data.push(chunk.toString()); 130 | }).on('error', done.fail).on('end', () => { 131 | expect(data[0].trim()).toEqual('title\n\nsubtitle\n\ndescription'); 132 | done(); 133 | }); 134 | }); 135 | 136 | it('should return an empty string for a non-existent module', (done) => { 137 | const data = []; 138 | const transformer = createTransformer({ 139 | modules: { 140 | head: { 141 | template: path.resolve(__dirname, './templates/head-include-module-doesnt-exist.ejs') 142 | } 143 | } 144 | }); 145 | 146 | transformer({ 147 | title: 'title', 148 | }).on('data', (chunk) => { 149 | data.push(chunk.toString()); 150 | }).on('error', done.fail).on('end', () => { 151 | expect(data[0].trim()).toEqual('title'); 152 | done(); 153 | }); 154 | }); 155 | 156 | }); 157 | }); 158 | --------------------------------------------------------------------------------