├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── 404.html ├── Dockerfile ├── LICENSE ├── README.md ├── config ├── defaults.json └── defaults.json.guide.js ├── docs ├── config.md ├── cors.md ├── development.md ├── dnd.gif ├── import.md └── screenshot.png ├── images ├── close.svg ├── edit-pencil.svg ├── favicon.ico ├── jump-icon.svg ├── logo.png └── overlay-arrow.svg ├── index.html ├── index.js ├── package.json ├── scripts ├── ace │ ├── snippets │ │ └── swagger.snippet.js │ └── themes │ │ └── theme-atom_dark.js ├── analytics │ └── google.js ├── app.js ├── bootstrap.js ├── branding.js ├── components.js ├── config.js ├── controllers │ ├── choose-mode.js │ ├── confirm-reset.js │ ├── editor.js │ ├── errorpresenter.js │ ├── general-modal.js │ ├── header.js │ ├── import-file.js │ ├── importurl.js │ ├── main.js │ ├── modal.js │ ├── openexamples.js │ ├── preferences.js │ └── preview.js ├── directives │ ├── auto-focus.js │ ├── collapsewhen.js │ ├── on-file-change.js │ ├── schemamodel.js │ ├── scroll-into-view-when.js │ ├── stop-event.js │ └── track-event.js ├── enums │ ├── defaults.js │ └── strings.js ├── filters │ └── formdata.js ├── plugins │ └── jquery.scroll-into-view.js ├── router.js ├── services │ ├── analytics.js │ ├── ast-manager.js │ ├── autocomplete.js │ ├── builder.js │ ├── editor.js │ ├── external-hooks.js │ ├── fileloader.js │ ├── focused-path.js │ ├── fold-state-manager.js │ ├── json-schema.js │ ├── keyword-map.js │ ├── local-storage.js │ ├── preferences.js │ ├── storage.js │ ├── sway-worker.js │ └── yaml.js └── workers │ ├── sway.worker.js │ └── yaml.worker.js ├── server.js ├── spec-files ├── cf-deployment.yml ├── concourse.yml ├── guide.yml ├── jmeter-storm-mode.yml └── jmeter-tornado-mode.yml ├── styles ├── branding.css ├── buttons.less ├── colors.less ├── components.less ├── components │ ├── collapse-when.less │ ├── editor.less │ ├── error-presenter.less │ ├── header.less │ ├── intro.less │ ├── menu-bar.less │ ├── modal.less │ ├── operation.less │ ├── path-headers.less │ ├── path.less │ ├── preferences-modal.less │ ├── preview.less │ ├── release.less │ ├── schema-model.less │ ├── security-definitions.less │ ├── stemcell.less │ ├── tags.less │ ├── tooltip.less │ ├── try-operation.less │ └── variable.less ├── fonts.less ├── main.less ├── markdown.less └── print.less ├── templates ├── about.html ├── addons-tags.html ├── addons.html ├── choose-mode.html ├── error-presenter.html ├── file-import.html ├── import.html ├── instance-groups.html ├── intro.html ├── open-examples.html ├── path.html ├── preferences.html ├── releases.html ├── reset-editor.html ├── schema-model.html ├── specs-info.html ├── stemcells.html ├── update-block.html ├── url-import.html └── variables.html ├── test ├── .eslintrc.js ├── e2e │ ├── .eslintrc.js │ ├── protractor.config.js │ └── specs │ │ ├── 1_console_test.js │ │ ├── 2_navigation_test.js │ │ ├── 3_rendering_test.js │ │ ├── 4_errors_test.js │ │ ├── 5_session_auth_test.js │ │ ├── 6_examples_test.js │ │ ├── 7_try_operation_test.js │ │ └── session.yaml └── unit │ ├── bootstrap.js │ ├── defaults.js │ ├── index.js │ ├── karma.conf.js │ ├── runner.html │ └── spec │ ├── controllers │ ├── editor.js │ ├── errorpresenter.js │ ├── file-import.js │ ├── main.js │ ├── openexamples.js │ ├── preview.js │ ├── tryoperation │ │ ├── generateUrl.js │ │ ├── getHeaders.js │ │ ├── getRequestBody.js │ │ ├── hasRequestBody.js │ │ ├── isCrossOrigin.js │ │ ├── isJson.js │ │ ├── isType.js │ │ ├── makeCall.js │ │ └── makeRequestModel.js │ └── url-import.js │ ├── directives │ ├── collapsewhen.js │ └── schemamodel.js │ └── services │ ├── ast-manager.js │ ├── autocomplete.js │ ├── backend.js │ ├── builder.js │ ├── editor.js │ ├── fileloader.js │ ├── fold-state-manager.js │ ├── json-schema.js │ └── storage.js ├── views ├── editor │ └── editor.html ├── header │ └── header.html ├── main.html └── preview │ └── preview.html └── webpack.config.js /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | .*.swp 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | dist/**/* 3 | scripts/analytics/google.js 4 | scripts/plugins/* 5 | scripts/ace/themes/theme-atom_dark.js 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'google', 3 | quotes: [2, 'single'], 4 | globals: { 5 | SwaggerEditor: false 6 | }, 7 | env: { 8 | browser: true 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .tmp 3 | node_modules/grunt-newer/.cache 4 | .sass-cache 5 | .grunt 6 | npm-debug.log 7 | dist/ 8 | node_modules 9 | *.sublime-workspace 10 | .*.swp 11 | buttons.diff 12 | .idea 13 | *.iml 14 | *.zip 15 | -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
144 |

Not found :(

145 |

Sorry, but the page you were trying to view does not exist.

146 |

It looks like this was the result of either:

147 | 151 | 154 | 155 |
156 | 157 | 158 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ### 2 | # swagger-editor - https://github.com/swagger-api/swagger-editor/ 3 | # 4 | # Run the swagger-editor service on port 8080 5 | ### 6 | 7 | FROM mhart/alpine-node 8 | 9 | RUN npm install -g http-server 10 | 11 | WORKDIR /editor 12 | ADD ./ /editor 13 | 14 | # The default port of the application 15 | EXPOSE 8080 16 | 17 | CMD ["http-server", "--cors", "-p8080", "/editor"] 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); 2 | you may not use this file except in compliance with the License. 3 | You may obtain a copy of the License at [apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) 4 | 5 | Unless required by applicable law or agreed to in writing, software 6 | distributed under the License is distributed on an "AS IS" BASIS, 7 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | See the License for the specific language governing permissions and 9 | limitations under the License. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BOSH Editor 2 | 3 | This is a fork of swagger editor. It modifies it to adapt to [BOSH](https://bosh.io/) manifests. 4 | 5 | Ongoing Work. Find a sample installation here : [http://bosh.jamilshamy.me/](http://bosh.jamilshamy.me/) 6 | -------------------------------------------------------------------------------- /config/defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "analytics": { 3 | "google": { 4 | "id": "helloooooooo" 5 | } 6 | }, 7 | "examplesFolder": "spec-files/", 8 | "editorOptions": { 9 | "theme": "ace/theme/atom_dark" 10 | }, 11 | "exampleFiles": [ 12 | "cf-deployment.yml", 13 | "jmeter-storm-mode.yml", 14 | "jmeter-tornado-mode.yml", 15 | "concourse.yml" 16 | ], 17 | "autoCompleteMode" : "Deployment-Manifest", 18 | "autoCompleteModes": [ 19 | "Deployment-Manifest", 20 | "Runtime-Config" 21 | ], 22 | "autocompleteExtension": {}, 23 | "keyPressDebounceTime": 200, 24 | "disableFileMenu": false, 25 | "headerBranding": false, 26 | "disableNewUserIntro": false, 27 | "enableTryIt": true, 28 | "brandingCssClass": "", 29 | "importProxyUrl": "https://example.com/", 30 | "pointerResolutionBasePath": null 31 | } 32 | -------------------------------------------------------------------------------- /config/defaults.json.guide.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** ***************************************************************************** 4 | * This is a guide for defaults.json configuration file. Please don't modify this 5 | * file for changing the settings, instead, modify defaults.json. 6 | * If you are using Swagger Editor as a dependency, Swagger Editor will make an 7 | * XHR request to '/config/defaults.json' to get it's settings. 8 | *******************************************************************************/ 9 | 10 | /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "defaults" }]*/ 11 | var defaults = { 12 | /* 13 | * Analytics section is used for user tracking configurations. At the moment 14 | * only Google Analytics is supported. 15 | */ 16 | analytics: { 17 | google: { 18 | /* 19 | * Put your Google Analytics ID here 20 | */ 21 | id: 'YOUR_GOOGLE_ANALYTICS_ID' 22 | } 23 | }, 24 | 25 | /* 26 | * Folder that example files are located 27 | * Note that this string will be used in between two other url segments 28 | * so you always need the trailing and leading slashes 29 | */ 30 | examplesFolder: 'spec-files/', 31 | 32 | /* 33 | * Ace editor options. This object will overload existing editor options. 34 | * See all possible options here: http://ace.c9.io/#nav=api&api=ace 35 | */ 36 | editorOptions: {}, 37 | 38 | /* 39 | * List of example files to show to user to pick from. The URL to fetch each 40 | * example is a combination of `examplesFolder` and file name 41 | */ 42 | exampleFiles: [ 43 | 'default.yaml', 44 | 'heroku-pets.yaml', 45 | 'minimal.yaml', 46 | 'petstore_simple.yaml', 47 | 'petstore_full.yaml', 48 | 'basic-auth.yaml', 49 | 'security.yaml' 50 | ], 51 | 52 | /* 53 | * Keywords for auto-complete are generated from a JavaScript object. 54 | * See keyword-map.js for object format 55 | */ 56 | autocompleteExtension: {}, 57 | 58 | /* 59 | * Change the default auto-complete mode 60 | */ 61 | autoCompleteMode: 'Deployment-Manifest', 62 | 63 | /* 64 | * Change how many milliseconds after the last keypress the editor should 65 | * respond to change. 66 | */ 67 | keyPressDebounceTime: 200, 68 | 69 | /* 70 | * Disables File menu which includes New, Open Example and Import commands 71 | */ 72 | disableFileMenu: false, 73 | 74 | /* 75 | * When it's enabled: 76 | * * Editor will append `brandingCssClass` class to body tag 77 | * * Editor will include branding templates at 78 | * app/templates/branding-left.html and 79 | * app/templates/branding-left.html 80 | * to it's header 81 | */ 82 | headerBranding: false, 83 | 84 | /* 85 | * Enables Try Operation functionality 86 | */ 87 | enableTryIt: true, 88 | 89 | /* 90 | * When `headerBranding` is enabled, this will be appended to body tag 91 | */ 92 | brandingCssClass: '', 93 | 94 | /* 95 | * Disables the overlay introduction panel 96 | */ 97 | disableNewUserIntro: false, 98 | 99 | /* 100 | * When Editor imports a file from a URL, it will prepend this URL to make 101 | * it possible to import contents that are not allowed to be loaded from a 102 | * different origin. If you're hosting your own editor, please replace this 103 | */ 104 | importProxyUrl: 'https://cors-it.herokuapp.com/?url=', 105 | 106 | /* 107 | * Use this base path for resolving JSON Pointers ($ref). 108 | * This value should be a valid URL. 109 | * 110 | * Example: http://example.com/swaggers 111 | * 112 | * More info: https://github.com/swagger-api/swagger-editor/issues/977#issuecomment-232254578 113 | */ 114 | pointerResolutionBasePath: null 115 | }; 116 | -------------------------------------------------------------------------------- /docs/config.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Configuration 4 | ============= 5 | 6 | Swagger editor is configured from the file [`config/defaults.json`](../config/defaults.json). 7 | To learn more about this file, please review [`config/defaults.json.guide.js`](../config/defaults.json.guide.js). 8 | 9 | Custom UI 10 | --------- 11 | 12 | You can also enable `headerBranding` flag and serve `/templates/branding-left.html` 13 | and `/templates/branding-right.html` files to have custom header. 14 | 15 | It's possible to serve a custom CSS file at `/styles/branding.css` path to override editor's appearances. 16 | 17 | It's also possible to serve a custom JavaScript file at `/scripts/branding.js` to add 18 | new functionalities to Swagger Editor. Using branding HTML pieces and branding JavaScript 19 | file you can add new controllers to Swagger Editor. 20 | 21 | #### `disableFileMenu` 22 | Set to `true` to disable the editor menu 23 | 24 | #### `editorOptions` 25 | Ace editor options. This object will overload existing editor options. 26 | See all possible options [here](http://ace.c9.io/#nav=api&api=ace) 27 | 28 | #### `keyPressDebounceTime` 29 | Change how many milliseconds after the last keypress the editor should respond to change. Defaults to `200ms`. 30 | 31 | #### `disableNewUserIntro` 32 | Disables the overlay introduction panel. It's enabled by default. 33 | 34 | External Hooks 35 | -------------- 36 | 37 | Swagger Editor provides an API for executing arbitrary code on certain events. 38 | 39 | To install a hook simply use `SwaggerEditor.on()` method. `.on()` method accepts two arguments, 40 | the first argument is the event name and the second argument is callback function that will be invoked when 41 | that event occurs. 42 | 43 | Here is a list of available event names: 44 | 45 | * `'code-change'` 46 | * `'put-success'` 47 | * `'put-failure` 48 | 49 | #### Example usage of external hooks 50 | ```js 51 | SwaggerEditor.on('put-failure', function() { 52 | alert('There was something wrong with saving your document.'); 53 | }); 54 | ``` 55 | 56 | Backends 57 | -------- 58 | 59 | #### `backendEndpoint` 60 | Url to a backend which supports `GET` for retrieving a OpenAPI Spec to edit 61 | and `PUT` for saving it. 62 | 63 | #### `useBackendForStorage` 64 | Set to ``true`` to enable a backend. 65 | 66 | #### `backendThrottle` 67 | The timeout for throttling backend calls. The default is 200 milliseconds 68 | 69 | #### `useYamlBackend` 70 | Set to ``true`` if the backend expects YAML, ``false`` will use JSON 71 | 72 | Analytics 73 | --------- 74 | `analytics` section in JSON configuration is used for user tracking configurations. At the moment only Google Analytics is supported. 75 | 76 | Example: 77 | 78 | ```js 79 | analytics: { 80 | google: { 81 | /* 82 | * Put your Google Analytics ID here 83 | */ 84 | id: 'YOUR_GOOGLE_ANALYTICS_ID' 85 | } 86 | } 87 | ``` 88 | 89 | Code Generation 90 | --------------- 91 | 92 | #### `disableCodeGen` 93 | Set to ``true`` to hide codegen links for clients and servers. 94 | 95 | #### `codegen` 96 | An object with keys ``servers``, ``clients``, ``server``, and ``client``. Each of with is a url to codegen service. 97 | 98 | 99 | Examples 100 | -------- 101 | 102 | #### `examplesFolder` 103 | Path to a directory with examples specs. Note that this string will be used in between two other URL segments so you always need the trailing and leading slashes 104 | 105 | #### `exampleFiles` 106 | Array of strings. List files in ``exampleFolder`` that contain example specs. The first file is used as the default document for the editor when it is opened. 107 | -------------------------------------------------------------------------------- /docs/cors.md: -------------------------------------------------------------------------------- 1 | # HTTP access control (CORS) issues 2 | 3 | Swagger Editor is a web application and by its nature is limited to [HTTP access control policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS). If you can't make calls using **Try this operation** component of the editor it's very likely because the server is not allowing the `editor.swagger.io` domain to make [`XHR`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) (also known as AJAX) calls to it. 4 | 5 | ## How to fix CORS issues 6 | 7 | There are multiple ways of fixing the CORS issue in Swagger Editor. You can modify your server if you own it or run Swagger Editor in an environment that ignores HTTP access control policy. 8 | 9 | ### Enable CORS in your server 10 | 11 | To enable CORS in your server you need to add following headers to your HTTP responses 12 | 13 | 14 | ``` 15 | Access-Control-Allow-Origin: editor.swagger.io 16 | Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept 17 | ``` 18 | 19 | Note that `Access-Control-Allow-Origin` accepts a regular expression. You can put `*` for its value to allow CORS calls from any domain(origin). 20 | 21 | ### Host Swagger Editor in your own domain 22 | 23 | If you don't want to add CORS headers to your server, the other option is to host Swagger Editor in your own domain. When Swagger Editor is running in the same domain as the API it's editing, it can make calls to your domain with no restriction. Run the following commands to generate a new build of Swagger Editor and serve the `dist` folder statically in your domain. 24 | 25 | When an XHR call is not cross-domain, JavaScript will see all the headers. If you want to see more accurate response, this method is preferred. 26 | 27 | ``` 28 | git clone https://github.com/swagger-api/swagger-editor.git 29 | cd swagger-editor 30 | npm run build 31 | 32 | # now dist folder have a fresh build of Swagger Editor 33 | ``` 34 | 35 | ### Proxy calls using a third party server 36 | 37 | You can make calls using a proxy to enable CORS for your API. Many API management providers provide proxies for enabling CORS. You can also run your own proxy. For example you can use [cors-it](https://github.com/mohsen1/cors-it) proxy if you're using Node.js. 38 | 39 | ### Run Swagger Editor in a browser that ignores HTTP access control 40 | 41 | Browsers have HTTP access control enabled by default but you can disable it in the setting. 42 | 43 | ##### Chrome 44 | To run Chrome with HTTP access control disabled use `--disable-web-security` flag. 45 | 46 | For example in OSX you need to run the following command: 47 | 48 | ``` 49 | /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --disable-web-security --user-data-dir 50 | ``` 51 | -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | # Development Guide 2 | 3 | ### Installing dependencies 4 | This app have npm dependencies. To install all dependencies, run 5 | ```shell 6 | npm install; 7 | ``` 8 | 9 | ### Specifying the port 10 | 11 | You can set the environment variable `PORT` to set the port 12 | 13 | ```shell 14 | PORT=81 npm start 15 | ``` 16 | 17 | ### Disallowing the browser to open 18 | 19 | Set `DO_NOT_OPEN` environment variable to start the server without 20 | opening the browser 21 | 22 | ```shell 23 | DO_NOT_OPEN=true npm start 24 | ``` 25 | 26 | ### Running in production mode 27 | 28 | Pass `--production` flag to `npm start` to run in production mode 29 | ```shell 30 | npm start --production 31 | 32 | ``` 33 | 34 | ### Building 35 | To build the project for production use, run: 36 | 37 | ```shell 38 | npm run build 39 | ``` 40 | This will build a new version of the web app, ready for production 41 | 42 | ### Configuration 43 | Swagger Editor will make an XHR GET call to `/config/defaults.json` to get it's settings before launch. If you are using Swagger Editor as a dependency or serving it statically, you can provide your own `defaults.json` at this endpoint to override default settings. 44 | 45 | Swagger Editor is configured with a file, [`defaults.json`](../app/config/defaults.json). 46 | Read the [configuration guide](./config.md) and additional details 47 | in [`defaults.json.guide.js`](../app/config/defaults.json.guide.js) 48 | to learn how to configure Swagger Editor. 49 | 50 | 51 | ### Running with Docker 52 | If you are familiar with [Docker](https://www.docker.com/), a `Dockerfile` is 53 | provided. 54 | 55 | Build an image named `swagger-editor` 56 | ```shell 57 | sudo docker build -t swagger-editor . 58 | ``` 59 | 60 | Run the container, using the local port 8080 (you may change this to any available 61 | port). 62 | ```shell 63 | sudo docker run -ti -p 8080:8080 swagger-editor 64 | ``` 65 | And open [http://localhost:8080](http://localhost:8080) in your browser 66 | 67 | ### Code Style 68 | Code style is enforced by ESLint. Build will fail if changes in code is not following code style guildlines. 69 | 70 | ### Testing 71 | To run all tests run 72 | 73 | ```shell 74 | npm test 75 | ``` 76 | 77 | This will build and run unit tests then if it was successful, it will run end-to-end tests. 78 | 79 | #### Unit tests 80 | All unit tests are located in [`../test/unit`](../test/unit). Unit tests are written in Jasmine and run by Karma. To run unit tests, run 81 | 82 | ```shell 83 | npm run unit-test 84 | ``` 85 | 86 | For developing unit tests, run 87 | ```shell 88 | npm run unit-test-watch 89 | ``` 90 | This will keep test browser and test watcher open and watches for file changes to re-run tests. 91 | 92 | #### End-to-end tests 93 | All end-to-end tests are located in [`../test/e2e`](../test/e2e). To run end-to-end test, run 94 | 95 | ```shell 96 | grunt protr 97 | npm run e2e-test 98 | This will run [Protractor](http://angular.github.io/protractor/#/) end-to-end test. 99 | -------------------------------------------------------------------------------- /docs/dnd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamlo/bosh-editor/a3b27e037feb5f18dff208f446daca8b526b4eb2/docs/dnd.gif -------------------------------------------------------------------------------- /docs/import.md: -------------------------------------------------------------------------------- 1 | # Importing OpenAPI Specs 2 | 3 | Swagger Editor can import your OpenAPI Spec file. OpenAPI Specs can be in JSON or YAML. 4 | 5 | ### File → Import File 6 | 7 | Click **Choose File** and select import. The file you are importing have to be valid JSON or YAML OpenAPI Spec file. Swagger Editor will prompt you about validation errors. 8 | 9 | ### File → Import URL 10 | 11 | Paste in URL to your OpenAPI Spec file. Swagger Editor will use a CORS proxy to download files that are being served with a server that doesn't support XHR calls from other domains. This might not work for locally hosted files, or files that are behind a firewall. Turn the **Use CORS proxy** check-box to disable this feature. 12 | 13 | ### URL Query Parameter 14 | 15 | You can also import an existing YAML or JSON Swagger 2.0 specs document by using the `import` query parameter. 16 | 17 | Note that the query parameter is in fragment section of the URL. After `/#/?`. It's because Swagger Editor is importing the file in the client side. 18 | 19 | For example: 20 | ``` 21 | http://editor.swagger.io/#/?import=http://generator.swagger.io/api/swagger.json 22 | ``` 23 | 24 | Swagger Editor will try to use a CORS proxy server for downloading your YAML. To disable this proxy server pass `no-proxy` query parameter 25 | 26 | ``` 27 | http://editor.swagger.io/#/?import=localhost:8000/swagger.yaml&no-proxy 28 | ``` 29 | 30 | ### Drag and Drop 31 | 32 | Simply drop your Swagger JSON or YAML files into Swagger Editor browser window. 33 | 34 | ![Swagger Editor drag and drop demo](./dnd.gif) 35 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamlo/bosh-editor/a3b27e037feb5f18dff208f446daca8b526b4eb2/docs/screenshot.png -------------------------------------------------------------------------------- /images/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /images/edit-pencil.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 12 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamlo/bosh-editor/a3b27e037feb5f18dff208f446daca8b526b4eb2/images/favicon.ico -------------------------------------------------------------------------------- /images/jump-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamlo/bosh-editor/a3b27e037feb5f18dff208f446daca8b526b4eb2/images/logo.png -------------------------------------------------------------------------------- /images/overlay-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BOSH Editor 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Polyfills 4 | require('es6-promise').polyfill(); 5 | require('es6-shim'); 6 | 7 | // App CSS files 8 | require('font-awesome-webpack'); 9 | require('styles/main.less'); 10 | 11 | // App JavaScript files 12 | require('scripts/app.js'); 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swagger-editor", 3 | "version": "2.10.4", 4 | "homepage": "https://github.com/swagger-api/swagger-editor", 5 | "description": "Swagger Editor", 6 | "license": "Apache-2.0", 7 | "files": [ 8 | "index.html", 9 | "styles/branding.css", 10 | "scripts/branding.js", 11 | "dist/", 12 | "config/", 13 | "images/", 14 | "spec-files/", 15 | "robots.txt", 16 | "404.html", 17 | "README.md" 18 | ], 19 | "authors": [ 20 | "Mohsen Azimi ", 21 | "Sahar Jafari " 22 | ], 23 | "repository": { 24 | "type": "github", 25 | "url": "swagger-api/swagger-editor.git" 26 | }, 27 | "devDependencies": { 28 | "angular": "^1.5.7", 29 | "angular-cookies": "^1.5.7", 30 | "angular-json-schema-form": "^0.1.4", 31 | "angular-marked": "^1.2.1", 32 | "angular-mocks": "^1.5.7", 33 | "angular-sanitize": "^1.5.7", 34 | "angular-ui-ace": "^0.2.3", 35 | "angular-ui-bootstrap": "^1.2.5", 36 | "angular-ui-layout": "^1.4.2", 37 | "angular-ui-router": "^0.3.1", 38 | "bootstrap": "^3.3.6", 39 | "brace": "^0.8.0", 40 | "chai": "^3.5.0", 41 | "css-loader": "^0.23.1", 42 | "es6-promise": "^3.1.2", 43 | "es6-shim": "^0.35.0", 44 | "eslint": "^2.13.1", 45 | "eslint-config-google": "^0.6.0", 46 | "eslint-friendly-formatter": "^2.0.3", 47 | "eslint-loader": "^1.3.0", 48 | "eslint-plugin-chai-expect": "^1.1.1", 49 | "extract-text-webpack-plugin": "^1.0.1", 50 | "file-loader": "^0.9.0", 51 | "font-awesome": "^4.6.1", 52 | "font-awesome-webpack": "0.0.4", 53 | "html-loader": "^0.4.3", 54 | "http-server": "^0.9.0", 55 | "jasmine-core": "^2.4.1", 56 | "jquery": "^3.0.0", 57 | "js-yaml": "^3.5.5", 58 | "json-editor": "^0.7.26", 59 | "json-formatter-js": "^1.0.0", 60 | "json-loader": "^0.5.4", 61 | "json-schema-view-js": "^0.4.1", 62 | "jsonformatter": "^0.6.0", 63 | "jsonlint": "^1.6.2", 64 | "karma": "^1.1.0", 65 | "karma-chai-plugins": "^0.7.0", 66 | "karma-chrome-launcher": "^1.0.1", 67 | "karma-firefox-launcher": "^1.0.0", 68 | "karma-jasmine": "^1.0.2", 69 | "karma-mocha": "^1.1.1", 70 | "karma-mocha-reporter": "^2.0.4", 71 | "karma-ng-html2js-preprocessor": "^1.0.0", 72 | "karma-ng-scenario": "^1.0.0", 73 | "karma-sourcemap-loader": "^0.3.7", 74 | "karma-webpack": "^1.7.0", 75 | "less": "^2.6.1", 76 | "less-loader": "^2.2.2", 77 | "lodash": "^4.6.1", 78 | "minimist": "^1.2.0", 79 | "mocha": "^2.4.5", 80 | "mversion": "^1.10.1", 81 | "ng-annotate-webpack-plugin": "^0.1.3", 82 | "ng-file-upload": "^12.0.4", 83 | "ngstorage": "^0.3.10", 84 | "open": "0.0.5", 85 | "open-sans-fontface": "^1.4.0", 86 | "protractor": "^3.2.1", 87 | "sinon": "^1.17.3", 88 | "sinon-chai": "^2.8.0", 89 | "source-code-pro": "https://github.com/adobe-fonts/source-code-pro.git#release", 90 | "spark-md5": "^2.0.2", 91 | "style-loader": "^0.13.0", 92 | "svg-inline-loader": "^0.6.0", 93 | "sway": "^1.0.0", 94 | "url-loader": "^0.5.7", 95 | "webpack": "^1.12.14", 96 | "webpack-dev-server": "^1.14.1", 97 | "worker-loader": "^0.7.0", 98 | "yaml-js": "^0.1.3" 99 | }, 100 | "engines": { 101 | "node": ">=5.8.0" 102 | }, 103 | "scripts": { 104 | "clean": "rm -rf dist/**", 105 | "prebuild": "npm run clean", 106 | "build": "webpack --production --hide-modules --stats=errors-only", 107 | "unit-test": "karma start test/unit/karma.conf.js --single-run=true", 108 | "unit-test-watch": "karma start test/unit/karma.conf.js", 109 | "pree2e-test": "webdriver-manager update", 110 | "e2e-test": "protractor test/e2e/protractor.config.js", 111 | "test": "npm run unit-test && npm run e2e-test", 112 | "lint": "eslint . --format 'node_modules/eslint-friendly-formatter'", 113 | "gh-pages": "./gh-pages.sh" 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /scripts/ace/themes/theme-atom_dark.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | window.ace.define("ace/theme/atom_dark",["require","exports","module","ace/lib/dom"], 4 | function(e,t,n){t.isDark=!0,t.cssClass="ace-atom-dark",t.cssText=".ace-atom-dark .ace_gutter {background: #1a1a1a;color: #868989}.ace-atom-dark .ace_print-margin {width: 1px;background: #1a1a1a}.ace-atom-dark {background-color: #1d1f21;color: #A8FF60}.ace-atom-dark .ace_cursor {color: white}.ace-atom-dark .ace_marker-layer .ace_selection {background: #444444}.ace-atom-dark.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #000000;border-radius: 2px}.ace-atom-dark .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}.ace-atom-dark .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #888888}.ace-atom-dark .ace_marker-layer .ace_highlight {border: 1px solid rgb(110, 119, 0);border-bottom: 0;box-shadow: inset 0 -1px rgb(110, 119, 0);margin: -1px 0 0 -1px;background: rgba(255, 235, 0, 0.1);}.ace-atom-dark .ace_marker-layer .ace_active-line {background: #2A2A2A}.ace-atom-dark .ace_gutter-active-line {background-color: #2A2A2A}.ace-atom-dark .ace_stack {background-color: rgb(66, 90, 44)}.ace-atom-dark .ace_marker-layer .ace_selected-word {border: 1px solid #888888}.ace-atom-dark .ace_invisible {color: #343434}.ace-atom-dark .ace_keyword,.ace-atom-dark .ace_meta,.ace-atom-dark .ace_storage,.ace-atom-dark .ace_storage.ace_type,.ace-atom-dark .ace_support.ace_type {color: #96CBFE}.ace-atom-dark .ace_keyword.ace_operator {color: #70C0B1}.ace-atom-dark .ace_constant.ace_character,.ace-atom-dark .ace_constant.ace_language,.ace-atom-dark .ace_constant.ace_numeric,.ace-atom-dark .ace_keyword.ace_other.ace_unit,.ace-atom-dark .ace_support.ace_constant,.ace-atom-dark .ace_variable.ace_parameter {color: #fe73fd}.ace-atom-dark .ace_constant.ace_other {color: #EEEEEE}.ace-atom-dark .ace_invalid {color: #CED2CF;background-color: #DF5F5F}.ace-atom-dark .ace_invalid.ace_deprecated {color: #CED2CF;background-color: #B798BF}.ace-atom-dark .ace_fold {background-color: #7AA6DA;border-color: #DEDEDE}.ace-atom-dark .ace_entity.ace_name.ace_function,.ace-atom-dark .ace_support.ace_function,.ace-atom-dark .ace_variable {color: #7AA6DA}.ace-atom-dark .ace_support.ace_class,.ace-atom-dark .ace_support.ace_type {color: #E7C547}.ace-atom-dark .ace_heading,.ace-atom-dark .ace_markup.ace_heading,.ace-atom-dark .ace_string {color: #B9CA4A}.ace-atom-dark .ace_entity.ace_name.ace_tag,.ace-atom-dark .ace_entity.ace_other.ace_attribute-name,.ace-atom-dark .ace_meta.ace_tag,.ace-atom-dark .ace_string.ace_regexp,.ace-atom-dark .ace_variable {color: #96CBFE}.ace-atom-dark .ace_comment {color: #7a7a7a}.ace-atom-dark .ace_c9searchresults.ace_keyword {color: #C2C280;}.ace-atom-dark .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYFBXV/8PAAJoAXX4kT2EAAAAAElFTkSuQmCC) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}) 5 | -------------------------------------------------------------------------------- /scripts/analytics/google.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function(i,s,o,g,r,a,m) {i['GoogleAnalyticsObject']=r;i[r]=i[r]||function() { 4 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 5 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 6 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 7 | -------------------------------------------------------------------------------- /scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | window.jQuery = require('jquery'); 4 | window.JSONFormatter = require('json-formatter-js'); 5 | var angular = require('angular'); 6 | 7 | // These modules do not export their module name 8 | // json-editor dependency is not in package.json of this package 9 | require('angular-json-schema-form'); 10 | require('json-editor'); 11 | require('ngstorage'); 12 | require('angular-ui-ace'); 13 | 14 | window.SwaggerEditor = angular.module('SwaggerEditor', [ 15 | require('angular-sanitize'), 16 | require('ng-file-upload'), 17 | require('angular-ui-router'), 18 | require('angular-ui-bootstrap'), 19 | require('angular-ui-layout'), 20 | require('angular-marked'), 21 | require('jsonformatter'), 22 | 'ui.ace', 23 | 'mohsen1.schema-form', 24 | 'ngStorage' 25 | ]); 26 | 27 | require('scripts/config.js'); 28 | 29 | // Require all of components 30 | require('scripts/components'); 31 | 32 | // Bootstrap the app 33 | require('scripts/bootstrap'); 34 | -------------------------------------------------------------------------------- /scripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | var $ = require('jquery'); 5 | var _ = require('lodash/string'); 6 | 7 | $(function() { 8 | // Try bootstrapping the app with embedded defaults if it exists 9 | var embeddedDefaults = window.$$embeddedDefaults; 10 | var pathname = window.location.pathname; 11 | 12 | if (!_.endsWith(pathname, '/')) { 13 | pathname += '/'; 14 | } 15 | 16 | var url = pathname + 'config/defaults.json'; 17 | 18 | if (embeddedDefaults) { 19 | bootstrap(embeddedDefaults); 20 | } else { 21 | $.getJSON(url).done(bootstrap).fail(function(error) { 22 | console.error('Failed to load defaults.json from', url); 23 | console.error(error); 24 | }); 25 | } 26 | 27 | /** 28 | * Bootstrap the application 29 | * @param {object} defaults - The defaults object 30 | * @return {undefined} 31 | */ 32 | function bootstrap(defaults) { 33 | window.SwaggerEditor.$defaults = defaults; 34 | 35 | SwaggerEditor.run(function($templateCache) { 36 | // require all templates 37 | $templateCache.put('templates/about.html', 38 | require('templates/about.html')); 39 | 40 | $templateCache.put('templates/error-presenter.html', 41 | require('templates/error-presenter.html')); 42 | 43 | $templateCache.put('templates/file-import.html', 44 | require('templates/file-import.html')); 45 | 46 | $templateCache.put('templates/import.html', 47 | require('templates/import.html')); 48 | 49 | $templateCache.put('templates/intro.html', 50 | require('templates/intro.html')); 51 | 52 | $templateCache.put('templates/open-examples.html', 53 | require('templates/open-examples.html')); 54 | 55 | $templateCache.put('templates/path.html', 56 | require('templates/path.html')); 57 | 58 | $templateCache.put('templates/releases.html', 59 | require('templates/releases.html')); 60 | 61 | $templateCache.put('templates/stemcells.html', 62 | require('templates/stemcells.html')); 63 | 64 | $templateCache.put('templates/update-block.html', 65 | require('templates/update-block.html')); 66 | 67 | $templateCache.put('templates/choose-mode.html', 68 | require('templates/choose-mode.html')); 69 | 70 | $templateCache.put('templates/preferences.html', 71 | require('templates/preferences.html')); 72 | 73 | $templateCache.put('templates/reset-editor.html', 74 | require('templates/reset-editor.html')); 75 | 76 | $templateCache.put('templates/schema-model.html', 77 | require('templates/schema-model.html')); 78 | 79 | $templateCache.put('templates/specs-info.html', 80 | require('templates/specs-info.html')); 81 | 82 | $templateCache.put('templates/addons-tags.html', 83 | require('templates/addons-tags.html')); 84 | 85 | $templateCache.put('templates/url-import.html', 86 | require('templates/url-import.html')); 87 | }); 88 | 89 | angular.bootstrap(window.document, ['SwaggerEditor']); 90 | } 91 | }); 92 | -------------------------------------------------------------------------------- /scripts/branding.js: -------------------------------------------------------------------------------- 1 | /* 2 | * branding.js can be used for overloading Swagger Editor behaviors 3 | * You can add new controllers to Swagger Editor with following syntax: 4 | * 5 | * SwaggerEditor.controller('BrandingController', function($scope) { ... }); 6 | * 7 | * You can use the controller you created in branding HTML pieces. 8 | */ 9 | -------------------------------------------------------------------------------- /scripts/components.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * A wrapper file that requires all different components. 4 | * 5 | * This file helps loading components easier in tests 6 | */ 7 | 8 | // Router 9 | require('scripts/router'); 10 | 11 | // Services 12 | require('scripts/services/analytics.js'); 13 | require('scripts/services/ast-manager.js'); 14 | require('scripts/services/autocomplete.js'); 15 | require('scripts/services/builder.js'); 16 | require('scripts/services/editor.js'); 17 | require('scripts/services/external-hooks.js'); 18 | require('scripts/services/fileloader.js'); 19 | require('scripts/services/focused-path.js'); 20 | require('scripts/services/fold-state-manager.js'); 21 | require('scripts/services/json-schema.js'); 22 | require('scripts/services/keyword-map.js'); 23 | require('scripts/services/local-storage.js'); 24 | require('scripts/services/preferences.js'); 25 | require('scripts/services/storage.js'); 26 | require('scripts/services/sway-worker.js'); 27 | require('scripts/services/yaml.js'); 28 | 29 | // Controllers 30 | require('scripts/controllers/editor.js'); 31 | require('scripts/controllers/errorpresenter.js'); 32 | require('scripts/controllers/general-modal.js'); 33 | require('scripts/controllers/header.js'); 34 | require('scripts/controllers/import-file.js'); 35 | require('scripts/controllers/importurl.js'); 36 | require('scripts/controllers/main.js'); 37 | require('scripts/controllers/modal.js'); 38 | require('scripts/controllers/openexamples.js'); 39 | require('scripts/controllers/choose-mode.js'); 40 | require('scripts/controllers/preferences.js'); 41 | require('scripts/controllers/preview.js'); 42 | require('scripts/controllers/confirm-reset.js'); 43 | 44 | // Directives 45 | require('scripts/directives/auto-focus.js'); 46 | require('scripts/directives/collapsewhen.js'); 47 | require('scripts/directives/on-file-change.js'); 48 | require('scripts/directives/schemamodel.js'); 49 | require('scripts/directives/scroll-into-view-when.js'); 50 | require('scripts/directives/stop-event.js'); 51 | require('scripts/directives/track-event.js'); 52 | 53 | // Enums 54 | require('scripts/enums/defaults.js'); 55 | require('scripts/enums/strings.js'); 56 | 57 | // Filter 58 | require('scripts/filters/formdata.js'); 59 | 60 | // Plugins 61 | require('scripts/plugins/jquery.scroll-into-view.js'); 62 | 63 | // Misc 64 | require('scripts/ace/snippets/swagger.snippet.js'); 65 | require('scripts/analytics/google.js'); 66 | 67 | // Ace Editor 68 | window.ace = require('brace'); 69 | 70 | -------------------------------------------------------------------------------- /scripts/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | SwaggerEditor.config(function Router($compileProvider, $logProvider, 6 | markedProvider) { 7 | var $cookies = angular.injector([require('angular-cookies')]).get('$cookies'); 8 | var isDevelopment = Boolean($cookies.get('swagger-editor-development-mode')); 9 | 10 | $compileProvider.aHrefSanitizationWhitelist('.'); 11 | 12 | $compileProvider.debugInfoEnabled(isDevelopment); 13 | $logProvider.debugEnabled(isDevelopment); 14 | 15 | markedProvider.setOptions({ 16 | sanitize: true 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /scripts/controllers/choose-mode.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerEditor.controller('ChooseModeCtrl', function ChooseModeCtrl($scope, 4 | $uibModalInstance, $rootScope, $state, Analytics, Preferences, defaults) { 5 | $scope.modes = defaults.autoCompleteModes; 6 | $scope.selectedMode = Preferences.get('autoCompleteMode'); 7 | 8 | $scope.save = function(mode) { 9 | Preferences.set('autoCompleteMode', mode); 10 | 11 | $uibModalInstance.close(); 12 | }; 13 | 14 | $scope.cancel = $uibModalInstance.close; 15 | }); 16 | -------------------------------------------------------------------------------- /scripts/controllers/confirm-reset.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerEditor.controller('ConfirmReset', function ConfirmReset($scope, 4 | $uibModalInstance, Editor) { 5 | $scope.cancel = $uibModalInstance.close; 6 | $scope.ok = function() { 7 | Editor.resetSettings(); 8 | $uibModalInstance.close(); 9 | }; 10 | }); 11 | -------------------------------------------------------------------------------- /scripts/controllers/editor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | SwaggerEditor.controller('EditorCtrl', function EditorCtrl($scope, $rootScope, 6 | Editor, Builder, Storage, ExternalHooks, Preferences) { 7 | var debouncedOnAceChange = getDebouncedOnAceChange(); 8 | 9 | // if user changed the preferences of keyPressDebounceTime, update the 10 | // debouncedOnAceChange function to have the latest debounce value 11 | Preferences.onChange(function(key) { 12 | if (key === 'keyPressDebounceTime') { 13 | debouncedOnAceChange = getDebouncedOnAceChange(); 14 | } 15 | }); 16 | 17 | /** 18 | * Get Debounced On Ace Change. 19 | * @return {function} returns debounced unchanged function 20 | */ 21 | function getDebouncedOnAceChange() { 22 | return _.debounce(onAceChange, Preferences.get('keyPressDebounceTime')); 23 | } 24 | 25 | $scope.aceLoaded = Editor.aceLoaded; 26 | 27 | $scope.aceChanged = function() { 28 | $rootScope.progressStatus = 'progress-working'; 29 | debouncedOnAceChange(); 30 | }; 31 | 32 | Editor.ready(function() { 33 | Storage.load('yaml').then(function(yaml) { 34 | $rootScope.editorValue = yaml; 35 | onAceChange(true); 36 | }); 37 | }); 38 | 39 | /** When there is a change on ace */ 40 | function onAceChange() { 41 | var value = $rootScope.editorValue; 42 | 43 | Storage.save('yaml', value); 44 | ExternalHooks.trigger('code-change', []); 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /scripts/controllers/general-modal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerEditor.controller('GeneralModal', function GeneralModal($scope, 4 | $uibModalInstance, data) { 5 | $scope.ok = $uibModalInstance.close; 6 | $scope.cancel = $uibModalInstance.close; 7 | $scope.data = data; 8 | }); 9 | -------------------------------------------------------------------------------- /scripts/controllers/import-file.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require("angular"); 4 | 5 | SwaggerEditor.controller('FileImportCtrl', function FileImportCtrl($scope, 6 | $uibModalInstance, $rootScope, $localStorage, $state, FileLoader, Storage) { 7 | var results; 8 | 9 | $scope.fileChanged = function($fileContent) { 10 | FileLoader.load($fileContent).then(function(res) { 11 | $scope.$apply(function() { 12 | results = res; 13 | }); 14 | }); 15 | }; 16 | 17 | $scope.ok = function() { 18 | if (angular.isString(results)) { 19 | $rootScope.editorValue = results; 20 | Storage.save('yaml', results); 21 | $state.go('home', {tags: null}); 22 | } 23 | $uibModalInstance.close(); 24 | }; 25 | 26 | $scope.isInvalidFile = function() { 27 | return results === null; 28 | }; 29 | 30 | $scope.isFileSelected = function() { 31 | return Boolean(results); 32 | }; 33 | 34 | $scope.cancel = $uibModalInstance.close; 35 | }); 36 | -------------------------------------------------------------------------------- /scripts/controllers/importurl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var angular = require('angular'); 5 | 6 | SwaggerEditor.controller('UrlImportCtrl', function FileImportCtrl($scope, 7 | $uibModalInstance, $localStorage, $rootScope, $state, FileLoader, Storage) { 8 | var results; 9 | 10 | $scope.url = null; 11 | $scope.error = null; 12 | $scope.opts = { 13 | useProxy: true 14 | }; 15 | 16 | var fetch = function(url) { 17 | $scope.error = null; 18 | $scope.canImport = false; 19 | 20 | if (_.startsWith(url, 'http')) { 21 | $scope.fetching = true; 22 | FileLoader.loadFromUrl(url, !$scope.opts.useProxy).then(function(data) { 23 | $scope.$apply(function() { 24 | results = data; 25 | $scope.canImport = true; 26 | $scope.fetching = false; 27 | }); 28 | }).catch(function(error) { 29 | $scope.$apply(function() { 30 | $scope.error = error; 31 | $scope.canImport = false; 32 | $scope.fetching = false; 33 | }); 34 | }); 35 | } else { 36 | $scope.error = 'Invalid URL'; 37 | } 38 | }; 39 | 40 | $scope.fetch = _.throttle(fetch, 200); 41 | 42 | $scope.ok = function() { 43 | if (angular.isString(results)) { 44 | Storage.save('yaml', results); 45 | $rootScope.editorValue = results; 46 | $state.go('home', {tags: null}); 47 | } 48 | $uibModalInstance.close(); 49 | }; 50 | 51 | $scope.cancel = $uibModalInstance.close; 52 | }); 53 | -------------------------------------------------------------------------------- /scripts/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var $ = require('jquery'); 4 | 5 | SwaggerEditor.controller('MainCtrl', function MainCtrl( 6 | $scope, $rootScope, $stateParams, $location, 7 | Editor, Storage, FileLoader, Analytics, defaults) { 8 | Analytics.initialize(); 9 | 10 | $rootScope.$on('$stateChangeStart', Editor.initializeEditor); 11 | 12 | // find a better way to add the branding class (grunt html template) (to do) 13 | $('body').addClass(defaults.brandingCssClass); 14 | 15 | /* 16 | * Assigns the YAML string to editor 17 | * 18 | * @param {string} yaml - the Swagger document YAML or JSON 19 | */ 20 | var assign = function(yaml) { 21 | if (yaml) { 22 | Storage.save('yaml', yaml); 23 | $rootScope.editorValue = yaml; 24 | } 25 | }; 26 | 27 | /* 28 | * Load Default or URL YAML 29 | */ 30 | var loadYaml = function() { 31 | Storage.load('yaml').then(function(yaml) { 32 | var url; 33 | var disableProxy = false; 34 | 35 | // If there is a url provided, override the storage with that URL 36 | if ($stateParams.import) { 37 | url = $stateParams.import; 38 | disableProxy = Boolean($stateParams['no-proxy']); 39 | $location.search('import', null); 40 | $location.search('no-proxy', null); 41 | 42 | // If there is no saved YAML either, load the default example 43 | } else if (!yaml) { 44 | url = defaults.examplesFolder + defaults.exampleFiles[0]; 45 | } 46 | 47 | if (url) { 48 | FileLoader.loadFromUrl(url, disableProxy).then(assign); 49 | } 50 | }); 51 | }; 52 | 53 | loadYaml(); 54 | 55 | $rootScope.$on('$stateChangeStart', loadYaml); 56 | 57 | // ----------------------- File drag and drop -------------------------------- 58 | 59 | var fileReader = new FileReader(); 60 | $scope.draggedFiles = []; 61 | 62 | // Watch for dropped files and trigger file reader 63 | $scope.$watch('draggedFiles', function() { 64 | if ($scope.draggedFiles instanceof File) { 65 | fileReader.readAsText($scope.draggedFiles, 'utf-8'); 66 | } 67 | }); 68 | 69 | // on reader success load the string 70 | fileReader.onloadend = function() { 71 | if (fileReader.result) { 72 | FileLoader.load(fileReader.result).then(assign); 73 | } 74 | }; 75 | }); 76 | -------------------------------------------------------------------------------- /scripts/controllers/modal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * General modal controller 5 | */ 6 | SwaggerEditor.controller('ModalCtrl', function ModalCtrl($scope, 7 | $uibModalInstance) { 8 | $scope.cancel = $uibModalInstance.close; 9 | $scope.close = $uibModalInstance.close; 10 | }); 11 | -------------------------------------------------------------------------------- /scripts/controllers/openexamples.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | SwaggerEditor.controller('OpenExamplesCtrl', function OpenExamplesCtrl($scope, 6 | $uibModalInstance, $rootScope, $state, FileLoader, Builder, Storage, 7 | Analytics, defaults) { 8 | $scope.files = defaults.exampleFiles; 9 | $scope.selectedFile = defaults.exampleFiles[0]; 10 | 11 | $scope.open = function(file) { 12 | // removes trailing slash from pathname because examplesFolder always have a 13 | // leading slash 14 | var pathname = _.endsWith(location.pathname, '/') ? 15 | location.pathname.substring(1) : 16 | location.pathname; 17 | 18 | var url = '/' + pathname + defaults.examplesFolder + file; 19 | 20 | FileLoader.loadFromUrl(url).then(function(value) { 21 | Storage.save('yaml', value); 22 | $rootScope.editorValue = value; 23 | $state.go('home', {tags: null}); 24 | $uibModalInstance.close(); 25 | }, $uibModalInstance.close); 26 | 27 | Analytics.sendEvent('open-example', 'open-example:' + file); 28 | }; 29 | 30 | $scope.cancel = $uibModalInstance.close; 31 | }); 32 | -------------------------------------------------------------------------------- /scripts/controllers/preferences.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerEditor.controller('PreferencesCtrl', function PreferencesCtrl($scope, 4 | $uibModalInstance, Preferences) { 5 | $scope.keyPressDebounceTime = Preferences.get('keyPressDebounceTime'); 6 | $scope.liveRender = Preferences.get('liveRender'); 7 | $scope.autoComplete = Preferences.get('autoComplete'); 8 | $scope.pointerResolutionBasePath = 9 | Preferences.get('pointerResolutionBasePath'); 10 | 11 | $scope.save = function() { 12 | var keyPressDebounceTime = parseInt($scope.keyPressDebounceTime, 10); 13 | if (keyPressDebounceTime > 0) { 14 | Preferences.set('keyPressDebounceTime', keyPressDebounceTime); 15 | } else { 16 | throw new Error('$scope.keyPressDebounceTime was not set correctly'); 17 | } 18 | 19 | Preferences.set('liveRender', $scope.liveRender); 20 | Preferences.set('autoComplete', $scope.autoComplete); 21 | Preferences.set('pointerResolutionBasePath', 22 | $scope.pointerResolutionBasePath); 23 | 24 | $uibModalInstance.close(); 25 | }; 26 | 27 | $scope.close = $uibModalInstance.close; 28 | }); 29 | -------------------------------------------------------------------------------- /scripts/directives/auto-focus.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // From https://gist.github.com/mlynch/dd407b93ed288d499778 4 | SwaggerEditor.directive('autoFocus', function($timeout) { 5 | return { 6 | restrict: 'A', 7 | link: function($scope, $element, $attributes) { 8 | $timeout(function() { 9 | $element[0].focus(); 10 | }, $attributes.autoFocus || 1); 11 | } 12 | }; 13 | }); 14 | -------------------------------------------------------------------------------- /scripts/directives/collapsewhen.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerEditor.directive('collapseWhen', function() { 4 | var TRANSITION_DURATION = 200; // ms 5 | 6 | return { 7 | restrict: 'A', 8 | link: function postLink(scope, element, attrs) { 9 | var buffer = null; 10 | 11 | var cleanUp = function() { 12 | // remove style attribute after animation 13 | // TDOD: just remove 'height' from style 14 | setTimeout(function() { 15 | element.removeAttr('style'); 16 | }, TRANSITION_DURATION); 17 | }; 18 | 19 | // If it's collapsed initially 20 | if (attrs.collapseWhen) { 21 | var clone = element.clone(); 22 | clone.removeAttr('style'); 23 | clone.appendTo('body'); 24 | buffer = clone.height(); 25 | clone.remove(); 26 | } 27 | 28 | scope.$watch(attrs.collapseWhen, function(val) { 29 | if (val) { 30 | buffer = element.height(); 31 | element.height(buffer); 32 | element.height(0); 33 | element.addClass('c-w-collapsed'); 34 | cleanUp(); 35 | } else { 36 | element.height(buffer); 37 | element.removeClass('c-w-collapsed'); 38 | cleanUp(); 39 | } 40 | }); 41 | } 42 | }; 43 | }); 44 | -------------------------------------------------------------------------------- /scripts/directives/on-file-change.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerEditor.directive('onReadFile', ['$parse', function($parse) { 4 | return { 5 | restrict: 'A', 6 | scope: false, 7 | link: function(scope, element, attrs) { 8 | var fn = $parse(attrs.onReadFile); 9 | 10 | element.on('change', function(onChangeEvent) { 11 | var reader = new FileReader(); 12 | 13 | reader.onload = function(onLoadEvent) { 14 | scope.$apply(function() { 15 | fn(scope, {$fileContent: onLoadEvent.target.result}); 16 | }); 17 | }; 18 | 19 | reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target) 20 | .files[0]); 21 | }); 22 | } 23 | }; 24 | }]); 25 | -------------------------------------------------------------------------------- /scripts/directives/schemamodel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var JSONFormatter = require('json-formatter-js'); 4 | var JSONSchemaView = require('json-schema-view-js'); 5 | 6 | SwaggerEditor.directive('schemaModel', function() { 7 | return { 8 | template: require('templates/schema-model.html'), 9 | restrict: 'E', 10 | replace: true, 11 | scope: { 12 | schema: '=' 13 | }, 14 | 15 | link: function postLink($scope, $element) { 16 | $scope.mode = 'schema'; 17 | 18 | $scope.switchMode = function() { 19 | $scope.mode = $scope.mode === 'json' ? 'schema' : 'json'; 20 | }; 21 | 22 | var render = function() { 23 | var formatter = new JSONFormatter($scope.schema, 1); 24 | $element.find('td.view.json').html(formatter.render()); 25 | 26 | var schemaView = new JSONSchemaView($scope.schema, 1); 27 | $element.find('td.view.schema').html(schemaView.render()); 28 | }; 29 | 30 | $scope.$watch('schema', render); 31 | 32 | render(); 33 | } 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /scripts/directives/scroll-into-view-when.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerEditor.directive('scrollIntoViewWhen', function() { 4 | return { 5 | restrict: 'A', 6 | link: function postLink($scope, $element, $attrs) { 7 | $scope.$watch($attrs.scrollIntoViewWhen, function(val) { 8 | if (val) { 9 | $element.scrollIntoView(100); 10 | } 11 | }); 12 | } 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /scripts/directives/stop-event.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // From http://stackoverflow.com/a/19846473/650722 4 | SwaggerEditor.directive('stopEvent', function() { 5 | return { 6 | restrict: 'A', 7 | link: function(scope, element) { 8 | element.bind('click', function(e) { 9 | e.stopPropagation(); 10 | }); 11 | } 12 | }; 13 | }); 14 | -------------------------------------------------------------------------------- /scripts/directives/track-event.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | /* 6 | * An attribute directive that will fire analytics event when element that this 7 | * directive is attached to is clicked 8 | * If the event name is separated this directive will fire subevents 9 | * 10 | * For example track-event="project new" will fire `project` event with `new` 11 | * subevent 12 | */ 13 | SwaggerEditor.directive('trackEvent', function(Analytics) { 14 | return { 15 | restrict: 'A', 16 | link: function($scope, $element, $attributes) { 17 | $element.bind('click', function() { 18 | var eventName = $attributes.trackEvent; 19 | 20 | if (angular.isString(eventName)) { 21 | // A Google Analytics event has three components: event category, 22 | // event action and event label; 23 | // we use 'click-item' as event action for this directive 24 | // an joined array of event hierarchy as event action 25 | // event label is set to window.location.origin to label events with 26 | // domain that they been fired from 27 | var eventCategory = 'click-item'; 28 | var eventAction = eventName.split(' ').join('->'); 29 | var eventLabel = window.location.origin; 30 | 31 | Analytics.sendEvent(eventCategory, eventAction, eventLabel); 32 | } 33 | }); 34 | } 35 | }; 36 | }); 37 | -------------------------------------------------------------------------------- /scripts/enums/defaults.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerEditor.config(function($provide) { 4 | $provide.constant('defaults', window.SwaggerEditor.$defaults); 5 | }); 6 | -------------------------------------------------------------------------------- /scripts/enums/strings.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerEditor.config(function($provide) { 4 | $provide.constant('strings', { 5 | 6 | // stausMessages keys should start with one of following words: 7 | // * error 8 | // * progress 9 | // * success 10 | // depending on starting word, the UI will appear with a different 11 | // appearance 12 | stausMessages: { 13 | 'error-connection': 'Server connection error', 14 | 'error-general': 'Error!', 15 | 'progress-working': 'Working...', 16 | 'progress-unsaved': 'Unsaved changes', 17 | 'success-process': 'Processed with no error', 18 | 'progress-saving': 'Saving...', 19 | 'success-saved': 'All changes saved', 20 | 'error-yaml': 'YAML Syntax Error', 21 | 'error-swagger': 'Swagger Error' 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /scripts/filters/formdata.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Renders a JavaScript object as form data 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var angular = require('angular'); 8 | 9 | SwaggerEditor.filter('formdata', function() { 10 | return function formdata(object) { 11 | var result = []; 12 | 13 | if (angular.isObject(object)) { 14 | Object.keys(object).forEach(function(key) { 15 | if (angular.isDefined(object[key])) { 16 | result.push(key + ': ' + object[key]); 17 | } 18 | }); 19 | } 20 | 21 | return result.join('\n'); 22 | }; 23 | }); 24 | -------------------------------------------------------------------------------- /scripts/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerEditor.config(function Router($stateProvider, 4 | $urlRouterProvider) { 5 | $urlRouterProvider.otherwise('/'); 6 | 7 | $stateProvider 8 | .state('home', { 9 | url: '/?import&tags&no-proxy', 10 | views: { 11 | '': { 12 | template: require('views/main.html'), 13 | controller: 'MainCtrl' 14 | }, 15 | 'header@home': { 16 | template: require('views/header/header.html'), 17 | controller: 'HeaderCtrl' 18 | }, 19 | 'editor@home': { 20 | template: require('views/editor/editor.html'), 21 | controller: 'EditorCtrl' 22 | }, 23 | 'preview@home': { 24 | template: require('views/preview/preview.html'), 25 | controller: 'PreviewCtrl' 26 | } 27 | } 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /scripts/services/analytics.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | SwaggerEditor.service('Analytics', function Analytics(defaults) { 5 | var isDisabled = false; 6 | var initialized = false; 7 | var id = _.defaults(defaults, { 8 | analytics: {google: {id: null}} 9 | }).analytics.google.id; 10 | 11 | /* 12 | * Initialize the analytics 13 | */ 14 | this.initialize = function() { 15 | var ga = window.ga; 16 | 17 | // disable if Google Analytics's `ga` global is not present or it is not 18 | // configured in default 19 | if (!window.ga || !id) { 20 | isDisabled = true; 21 | return; 22 | } 23 | 24 | // don't initialize more than once 25 | if (initialized) { 26 | return; 27 | } 28 | 29 | // Load the plugin. 30 | ga('require', 'linker'); 31 | 32 | // Define which domains to autoLink. 33 | ga('linker:autoLink', ['swagger.io']); 34 | 35 | ga('create', id, 'auto', {allowLinker: true}); 36 | 37 | ga('send', 'pageview'); 38 | 39 | initialized = true; 40 | }; 41 | 42 | /* 43 | * Expose event tracking calls. 44 | * This function can have unlimited number of arguments but usually three 45 | * arguments that are: 46 | * eventCategory: The category of the event. For example: selected-example 47 | * eventAction: The event action: For example: pet-store 48 | * eventLabel: The event label. This can be anything you want. 49 | */ 50 | this.sendEvent = function(/* eventCategory, eventAction, eventLabel*/) { 51 | if (isDisabled) { 52 | return; 53 | } 54 | 55 | if (!arguments.length) { 56 | throw new Error('sendEvent was called with no arguments'); 57 | } 58 | 59 | Array.prototype.unshift.call(arguments, 'event'); 60 | Array.prototype.unshift.call(arguments, 'send'); 61 | 62 | window.ga.apply(window.ga, arguments); 63 | }; 64 | }); 65 | -------------------------------------------------------------------------------- /scripts/services/builder.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var jsyaml = require('js-yaml'); 4 | var _ = require('lodash'); 5 | 6 | SwaggerEditor.service('Builder', function Builder(SwayWorker, Preferences) { 7 | var load = _.memoize(jsyaml.load); 8 | 9 | /* 10 | * Build spec docs from a string value 11 | * @param {string} stringValue - the string to make the docs from 12 | * @returns {promise} - Returns a promise that resolves to spec document 13 | * object or get rejected because of HTTP failures of external $refs 14 | */ 15 | var buildDocs = function(stringValue) { 16 | var json; 17 | 18 | return new Promise(function(resolve, reject) { 19 | // If stringVlue is empty, return emptyDocsError 20 | if (!stringValue) { 21 | reject({ 22 | specs: null, 23 | errors: [{emptyDocsError: 'Empty Document Error'}] 24 | }); 25 | } 26 | 27 | // if jsyaml is unable to load the string value return yamlError 28 | try { 29 | json = load(stringValue); 30 | } catch (yamlError) { 31 | reject({ 32 | errors: [{yamlError: yamlError}], 33 | specs: null 34 | }); 35 | } 36 | 37 | // Add `title` from object key to definitions 38 | // if they are missing title 39 | if (json && _.isObject(json.definitions)) { 40 | for (var definition in json.definitions) { 41 | if (_.isObject(json.definitions[definition]) && 42 | !_.startsWith(definition, 'x-') && 43 | _.isEmpty(json.definitions[definition].title)) { 44 | json.definitions[definition].title = definition; 45 | } 46 | } 47 | } 48 | 49 | SwayWorker.run({ 50 | definition: json, 51 | jsonRefs: { 52 | relativeBase: Preferences.get('pointerResolutionBasePath') 53 | } 54 | }, function(data) { 55 | if (data.errors.length) { 56 | reject(data); 57 | } else { 58 | resolve(data); 59 | } 60 | }); 61 | }); 62 | }; 63 | 64 | this.buildDocs = buildDocs; 65 | }); 66 | -------------------------------------------------------------------------------- /scripts/services/external-hooks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | /* 6 | * Provide external hooks to various events in Swagger Editor 7 | * 8 | * # Usage 9 | * SwaggerEditor.on(EventName, Callback) 10 | */ 11 | SwaggerEditor.service('ExternalHooks', function ExternalHooks() { 12 | // Hooks hash 13 | var hooks = { 14 | 'code-change': [], // triggers when code changes 15 | 'put-success': [], // triggers when PUT request to backend succeeds 16 | 'put-failure': [] // triggers when PUT request to backend fails 17 | }; 18 | 19 | /* 20 | * Hook install method 21 | * 22 | * @param {string} eventName 23 | * @param {function} callback - function to get triggered on event occurrence 24 | */ 25 | SwaggerEditor.on = function(eventName, callback) { 26 | if (!angular.isString(eventName)) { 27 | throw new TypeError('eventName must be string'); 28 | } 29 | 30 | if (!angular.isFunction(callback)) { 31 | throw new TypeError('callback must be a function'); 32 | } 33 | 34 | if (!hooks[eventName]) { 35 | throw new Error(eventName + ' is not a valid event name'); 36 | } 37 | 38 | var isRegisteredCallback = hooks[eventName].some(function(cb) { 39 | return cb === callback; 40 | }); 41 | 42 | if (!isRegisteredCallback) { 43 | hooks[eventName].push(callback); 44 | } 45 | }; 46 | 47 | /* 48 | * Triggers a hook 49 | * @param {string} eventName - event name to trigger 50 | * @param {array} args - arguments to trigger callback functions with 51 | */ 52 | this.trigger = function(eventName, args) { 53 | if (!angular.isString(eventName)) { 54 | throw new TypeError('eventName must be string'); 55 | } 56 | 57 | if (!angular.isArray(args)) { 58 | throw new TypeError('args must be an array'); 59 | } 60 | 61 | if (!hooks[eventName]) { 62 | throw new Error(eventName + ' is not a valid event name'); 63 | } 64 | 65 | hooks[eventName].forEach(function(callback) { 66 | callback.apply(null, args); 67 | }); 68 | }; 69 | }); 70 | -------------------------------------------------------------------------------- /scripts/services/fileloader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | var _ = require('lodash'); 5 | 6 | /* 7 | * File loader service to load file from a URL or string 8 | */ 9 | SwaggerEditor.service('FileLoader', function FileLoader($http, defaults, YAML) { 10 | /** 11 | * Load a file from URL 12 | * 13 | * @param {string} url - the URL to load from 14 | * @param {boolean} disableProxy - disables cors-it proxy 15 | * @return {Promise} - resolves to content of the file 16 | */ 17 | function loadFromUrl(url, disableProxy) { 18 | return new Promise(function(resolve, reject) { 19 | if (disableProxy === undefined) { 20 | disableProxy = false; 21 | } 22 | 23 | // Temporarily use this service to get around non-CORSable URLs 24 | if (_.startsWith(url, 'http') && !disableProxy) { 25 | url = defaults.importProxyUrl + url; 26 | } 27 | 28 | $http({ 29 | method: 'GET', 30 | url: url, 31 | headers: { 32 | accept: 'application/x-yaml,text/yaml,application/json,*/*' 33 | } 34 | }).then(function(resp) { 35 | if (angular.isObject(resp.data)) { 36 | YAML.dump(resp.data, function(error, yamlString) { 37 | if (error) { 38 | return reject(error); 39 | } 40 | 41 | resolve(yamlString); 42 | }); 43 | } else { 44 | load(resp.data).then(resolve, reject); 45 | } 46 | }, reject); 47 | }); 48 | } 49 | 50 | /** 51 | * takes a JSON or YAML string, returns YAML string 52 | * 53 | * @param {string} string - the JSON or YAML raw string 54 | * @return {Promise} YAML string 55 | * @throws {TypeError} - resolves to a YAML string 56 | */ 57 | function load(string) { 58 | return new Promise(function(resolve, reject) { 59 | if (!_.isString(string)) { 60 | throw new TypeError('load function only accepts a string'); 61 | } 62 | 63 | try { 64 | JSON.parse(string); 65 | } catch (error) { 66 | // Do not change to JSON if it is YAML, and 67 | // just resolve it 68 | resolve(string); 69 | return; 70 | } 71 | 72 | YAML.load(string, function(error, json) { 73 | if (error) { 74 | return reject(error); 75 | } 76 | 77 | YAML.dump(json, function(error, yamlString) { 78 | if (error) { 79 | return reject(error); 80 | } 81 | 82 | resolve(yamlString); 83 | }); 84 | }); 85 | }); 86 | } 87 | 88 | // Load from Local file content (string) 89 | this.load = load; 90 | this.loadFromUrl = loadFromUrl; 91 | }); 92 | -------------------------------------------------------------------------------- /scripts/services/focused-path.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | /* 6 | * Focused Path provider 7 | * using AST Manager and Editor information provides path to elements 8 | * that are in focus in the editor line of code to focus in the editor 9 | * for a given path 10 | */ 11 | SwaggerEditor.service('FocusedPath', function FcusedPath(ASTManager, Editor) { 12 | /* 13 | * A path is in focus if it's key or **any** sub path of it is in focus 14 | * @param {array} - an array of strings pointing to a node in specs tree 15 | * @returns {boolean} - If path is in focus returns true otherwise returns 16 | * false 17 | */ 18 | this.isInFocus = function(path) { 19 | var focusedLine = Editor.lineInFocus(); 20 | var focusedPath = ASTManager.pathForPosition(focusedLine); 21 | 22 | return Array.isArray(focusedPath) && 23 | _.isEqual(path, focusedPath.slice(0, path.length)); 24 | }; 25 | }); 26 | -------------------------------------------------------------------------------- /scripts/services/fold-state-manager.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | /** 6 | * Manages fold state of nodes in the YAML and preview pane 7 | * The state of the folds are kept in $rootScope.specs itself. 8 | */ 9 | SwaggerEditor.service('FoldStateManager', function FoldStateManager(ASTManager, 10 | Editor, $rootScope) { 11 | Editor.onFoldChanged(foldChangedInEditor); 12 | this.foldEditor = foldEditor; 13 | this.getFoldedTree = getFoldedTree; 14 | 15 | /** 16 | * Adds or removes a fold in Ace editor with given path 17 | * 18 | * @param {array} path - a list of keys to reach to the node that 19 | * needs to be folded/unfolded 20 | * 21 | * @param {boolean} fold - true to fold the node and false to unfold it 22 | * 23 | */ 24 | function foldEditor(path, fold) { 25 | ASTManager.positionRangeForPath($rootScope.editorValue, path) 26 | .then(function(range) { 27 | // Editor API is 0-indexed. Because of this we're subtracting 1 from line 28 | // numbers 29 | if (fold) { 30 | Editor.addFold(range.start.line - 1, range.end.line - 1); 31 | } else { 32 | Editor.removeFold(range.start.line - 1, range.end.line - 1); 33 | } 34 | }); 35 | } 36 | 37 | /** 38 | * Responder to fold change events in Ace editor 39 | * 40 | * @param {object} event - Ace editor's fold change event. It has a data 41 | * object that includes the location of the fold and an action property that 42 | * describes the type of event(fold or unfold) 43 | * 44 | */ 45 | function foldChangedInEditor(event) { 46 | // Editor API is 0-indexed. Because of this we're adding 1 to line numbers 47 | var position = { 48 | line: event.data.start.row + 1, 49 | column: event.data.start.column + 1 50 | }; 51 | 52 | ASTManager.pathForPosition($rootScope.editorValue, position) 53 | .then(function(path) { 54 | var $folded = event.action === 'add'; 55 | 56 | // walk down the tree to reach to our specific node in spec 57 | var current = $rootScope.specs; 58 | while (path.length && _.isObject(current)) { 59 | current = current[path.shift()]; 60 | } 61 | 62 | /* eslint no-implicit-coercion: [2, { "allow": ["!!", "~"] } ]*/ 63 | if (_.isObject(current)) { 64 | current.$folded = !!$folded; 65 | $rootScope.$apply(); 66 | } 67 | }); 68 | } 69 | 70 | /** 71 | * Get fold state tree of spec 72 | * 73 | * @param {object} tree - tree 74 | * @param {object} newTree - newTree 75 | * 76 | * @return {object} object 77 | */ 78 | function getFoldedTree(tree, newTree) { 79 | if (!tree) { 80 | return tree; 81 | } 82 | 83 | var result = {}; 84 | 85 | _.keys(tree).forEach(function(key) { 86 | if (_.isObject(tree[key]) && _.isObject(newTree[key])) { 87 | result[key] = getFoldedTree(tree[key], newTree[key]); 88 | } else if (key === '$folded') { 89 | result[key] = tree[key]; 90 | } else { 91 | result[key] = newTree[key]; 92 | } 93 | }); 94 | 95 | return result; 96 | } 97 | }); 98 | -------------------------------------------------------------------------------- /scripts/services/json-schema.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | var _ = require('lodash'); 5 | 6 | SwaggerEditor.service('JSONSchema', function JSONschema() { 7 | /** 8 | * Appends JSON Editor options for schema recursively so if a schema needs to 9 | * be edited by JSON Editor loosely it's possible 10 | * 11 | * @param {object} schema - A JSON Schema object 12 | * 13 | * @return {object} - A JSON Schema object 14 | */ 15 | function appendJSONEditorOptions(schema) { 16 | var looseOptions = { 17 | /*eslint-disable */ 18 | no_additional_properties: false, 19 | disable_properties: false, 20 | disable_edit_json: false 21 | /*eslint-enable */ 22 | }; 23 | 24 | // If schema is loose add options for JSON Editor 25 | if (isLooseJSONSchema(schema)) { 26 | schema.options = looseOptions; 27 | } 28 | 29 | _.each(schema.properties, appendJSONEditorOptions); 30 | 31 | return schema; 32 | } 33 | 34 | /** 35 | * Determines if a JSON Schema is loose 36 | * 37 | * @param {object} schema - A JSON Schema object 38 | * 39 | * @return {boolean} true if schema is a loose JSON 40 | */ 41 | function isLooseJSONSchema(schema) { 42 | // loose object 43 | if (schema.additionalProperties || _.isEmpty(schema.properties)) { 44 | return true; 45 | } 46 | 47 | // loose array of objects 48 | if ( 49 | schema.type === 'array' && 50 | (schema.items.additionalProperties || 51 | _.isEmpty(schema.items.properties)) 52 | ) { 53 | return true; 54 | } 55 | 56 | return false; 57 | } 58 | 59 | /** 60 | * Resolves all of `allOf` recursively in a schema 61 | * @description 62 | * if a schema has allOf it means that the schema is the result of mergin all 63 | * schemas in it's allOf array. 64 | * 65 | * @param {object} schema - JSON Schema 66 | * 67 | * @return {object} JSON Schema 68 | */ 69 | function resolveAllOf(schema) { 70 | if (schema.allOf) { 71 | schema = _.merge.apply(null, [schema].concat(schema.allOf)); 72 | delete schema.allOf; 73 | } 74 | 75 | if (_.isObject(schema.properties)) { 76 | schema.properties = _.keys(schema.properties) 77 | .reduce(function(properties, key) { 78 | properties[key] = resolveAllOf(schema.properties[key]); 79 | return properties; 80 | }, {}); 81 | } 82 | 83 | return schema; 84 | } 85 | 86 | /** 87 | * Fills in empty gaps of a JSON Schema. This method is mostly used to 88 | * normalize JSON Schema objects that are abstracted from Swagger parameters 89 | * 90 | * @param {object} schema - JSON Schema 91 | * 92 | * @return {object} - Normalized JSON Schema 93 | */ 94 | function normalizeJSONSchema(schema) { 95 | // provide title property if it's missing. 96 | if (!schema.title && angular.isString(schema.name)) { 97 | schema.title = schema.name; 98 | } 99 | 100 | schema = resolveAllOf(schema); 101 | 102 | // if schema is missing the "type" property fill it in based on available 103 | // properties 104 | if (!schema.type) { 105 | // it's an object if it has "properties" property 106 | if (schema.properties) { 107 | schema.type = 'object'; 108 | } 109 | 110 | // it's an array if it has "items" property 111 | if (schema.items) { 112 | schema.type = 'array'; 113 | } 114 | } 115 | 116 | // Swagger extended JSON Schema with a new type, file. If we see file type 117 | // we will add format: file to the schema so the form generator will render 118 | // a file input 119 | if (schema.type === 'file') { 120 | schema.type = 'string'; 121 | schema.format = 'file'; 122 | } 123 | 124 | return appendJSONEditorOptions(schema); 125 | } 126 | 127 | this.normalizeJSONSchema = normalizeJSONSchema; 128 | this.resolveAllOf = resolveAllOf; 129 | this.appendJSONEditorOptions = appendJSONEditorOptions; 130 | this.isLooseJSONSchema = isLooseJSONSchema; 131 | }); 132 | -------------------------------------------------------------------------------- /scripts/services/keyword-map.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var angular = require('angular'); 5 | SwaggerEditor.service('KeywordMap', function KeywordMap(Preferences, defaults) { 6 | /* eslint-disable */ 7 | /** 8 | * =================================================================================================================== 9 | * =================================================================================================================== 10 | * Deployment Manifest Schema 11 | */ 12 | var dm_release = { 13 | name: String, 14 | version: String 15 | }; 16 | 17 | var dm_stemcell = { 18 | alias: String, 19 | os: String, 20 | name: String, 21 | version: String 22 | }; 23 | 24 | var dm_updateBlock = { 25 | canaries: String, 26 | max_in_flight: String, 27 | canary_watch_time: String, 28 | update_watch_time: String, 29 | serial: Boolean 30 | }; 31 | 32 | var dm_job = { 33 | name: String, 34 | release: String, 35 | consumes: Object, 36 | provides: Object, 37 | properties: Object 38 | }; 39 | 40 | var dm_network = { 41 | name: String, 42 | static_ips: [String], 43 | "default": [String] 44 | }; 45 | 46 | var dm_instance_group = { 47 | name: String, 48 | instances: Number, 49 | lifecycle: String, 50 | azs: [String], 51 | vm_type: String, 52 | vm_extensions: [String], 53 | stemcell: String, 54 | migrated_from: String, 55 | networks: [dm_network], 56 | jobs: [dm_job] 57 | }; 58 | 59 | var dm_variable = { 60 | name: String, 61 | type: String, 62 | options: Object, 63 | }; 64 | 65 | var deploymentManifestMap = { 66 | name: String, 67 | director_uuid: String, 68 | releases: [dm_release], 69 | stemcells: [dm_stemcell], 70 | update: dm_updateBlock, 71 | instance_groups: [dm_instance_group], 72 | variables: [dm_variable] 73 | }; 74 | 75 | /** 76 | * =================================================================================================================== 77 | * =================================================================================================================== 78 | * Runtime Config Schema 79 | */ 80 | var rc_release = { 81 | name: String, 82 | version: String 83 | }; 84 | 85 | var rc_job = { 86 | name: String, 87 | release: String 88 | }; 89 | 90 | var rc_addon = { 91 | name: String, 92 | jobs: [rc_job], 93 | include: Object, 94 | properties: Object 95 | }; 96 | 97 | var runtimeConfigMap = { 98 | releases: [rc_release], 99 | addons: [rc_addon], 100 | tags: Object 101 | }; 102 | 103 | /* eslint-enable */ 104 | this.get = function() { 105 | var chosenMode = Preferences.get("autoCompleteMode"); 106 | var chosenMap = []; 107 | 108 | switch (chosenMode) { 109 | case "Deployment-Manifest": 110 | chosenMap = deploymentManifestMap; 111 | break; 112 | case "Runtime-Config": 113 | chosenMap = runtimeConfigMap; 114 | break; 115 | default: 116 | console.log("Sorry, mode '" + chosenMode + "' is not supported."); 117 | } 118 | 119 | var extension = angular.isObject(defaults.autocompleteExtension) ? 120 | defaults.autocompleteExtension : {}; 121 | return _.extend(chosenMap, extension); 122 | }; 123 | }); 124 | -------------------------------------------------------------------------------- /scripts/services/local-storage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var angular = require('angular'); 5 | 6 | SwaggerEditor.service('LocalStorage', function LocalStorage($localStorage, 7 | $rootScope) { 8 | var storageKey = 'SwaggerEditorCache'; 9 | var changeListeners = {}; 10 | 11 | $localStorage[storageKey] = $localStorage[storageKey] || {}; 12 | 13 | /* 14 | * 15 | */ 16 | var save = function(key, value) { 17 | if (value === null) { 18 | return; 19 | } 20 | 21 | if (Array.isArray(changeListeners[key])) { 22 | changeListeners[key].forEach(function(fn) { 23 | fn(value); 24 | }); 25 | } 26 | 27 | _.debounce(function() { 28 | window.requestAnimationFrame(function() { 29 | $localStorage[storageKey][key] = value; 30 | }); 31 | 32 | if (key === 'yaml') { 33 | $rootScope.progressStatus = 'success-saved'; 34 | } 35 | }, 100)(); 36 | }; 37 | 38 | /* 39 | * 40 | */ 41 | var load = function(key) { 42 | return new Promise(function(resolve) { 43 | if (key) { 44 | resolve($localStorage[storageKey][key]); 45 | } else { 46 | resolve($localStorage[storageKey]); 47 | } 48 | }); 49 | }; 50 | 51 | /* 52 | * 53 | */ 54 | var addChangeListener = function(key, fn) { 55 | if (angular.isFunction(fn)) { 56 | if (!changeListeners[key]) { 57 | changeListeners[key] = []; 58 | } 59 | changeListeners[key].push(fn); 60 | } 61 | }; 62 | 63 | this.save = save; 64 | this.reset = $localStorage.$reset; 65 | this.load = load; 66 | this.addChangeListener = addChangeListener; 67 | }); 68 | -------------------------------------------------------------------------------- /scripts/services/preferences.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var angular = require('angular'); 5 | 6 | /** 7 | * @ngdoc service 8 | * @name SwaggerEditor.preferences 9 | * @description 10 | * # preferences 11 | * Service in the phonicsApp. 12 | */ 13 | SwaggerEditor.service('Preferences', function Preferences($localStorage, 14 | defaults) { 15 | var changeListeners = []; 16 | 17 | var defaultPreferences = { 18 | 19 | /* 20 | * Update the preview pane per keypress if it's true, otherwise after value 21 | * change in the editor, a "Reload" button will show up in preview pane 22 | */ 23 | liveRender: true, 24 | 25 | /* 26 | * Disable/enable auto-compelte functionallity. 27 | */ 28 | autoComplete: true, 29 | 30 | /* 31 | * Auto-complete mpde. 32 | */ 33 | autoCompleteMode: defaults.autoCompleteMode, 34 | 35 | /* 36 | * Wait time for editor to react to keyboard events 37 | */ 38 | keyPressDebounceTime: defaults.keyPressDebounceTime, 39 | 40 | /* 41 | * JSON Pointer resolution base path 42 | */ 43 | pointerResolutionBasePath: defaults.pointerResolutionBasePath || 44 | location.origin + location.pathname 45 | }; 46 | 47 | var preferences = _.extend(defaultPreferences, $localStorage.preferences); 48 | 49 | var save = function() { 50 | $localStorage.preferences = preferences; 51 | }; 52 | 53 | this.get = function(key) { 54 | return preferences[key]; 55 | }; 56 | 57 | this.set = function(key, value) { 58 | if (value === undefined) { 59 | throw new TypeError('value was undefined'); 60 | } 61 | preferences[key] = value; 62 | save(); 63 | changeListeners.forEach(function(fn) { 64 | fn(key, value); 65 | }); 66 | }; 67 | 68 | this.reset = function() { 69 | preferences = defaultPreferences; 70 | save(); 71 | }; 72 | 73 | this.getAll = function() { 74 | return preferences; 75 | }; 76 | 77 | /* 78 | * A global change hook for preferences change 79 | * 80 | * @param {function} fn - the callback function 81 | */ 82 | this.onChange = function(fn) { 83 | if (angular.isFunction(fn)) { 84 | changeListeners.push(fn); 85 | } 86 | }; 87 | }); 88 | -------------------------------------------------------------------------------- /scripts/services/storage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerEditor.service('Storage', function Storage(LocalStorage) { 4 | return LocalStorage; 5 | }); 6 | -------------------------------------------------------------------------------- /scripts/services/sway-worker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * A simple queue for maintaining order of execution and accurate callback 5 | * calling for worker tasks 6 | * 7 | */ 8 | SwaggerEditor.service('SwayWorker', function SwayWorker() { 9 | var SwayWorker = require('scripts/workers/sway.worker.js'); 10 | var worker = new SwayWorker(); 11 | var queue = []; 12 | var currentTask = null; 13 | 14 | worker.onmessage = onMessage; 15 | worker.onerror = onError; 16 | 17 | /** 18 | * Schedule a task for the worker 19 | * 20 | * @param {obj} arg - the task arguments 21 | * 22 | * @param {function} cb - completion callback 23 | */ 24 | function schedule(arg, cb) { 25 | queue.push({ 26 | arg: arg, 27 | cb: cb 28 | }); 29 | enqueue(); 30 | } 31 | 32 | /** 33 | * Enqueue a task from task list and invoke it 34 | * 35 | * @private 36 | */ 37 | function enqueue() { 38 | // if queue is empty do nothing. 39 | if (!queue.length) { 40 | return; 41 | } 42 | 43 | // if there is a currentTask do nothing 44 | if (currentTask) { 45 | return; 46 | } 47 | 48 | currentTask = queue.shift(); 49 | worker.postMessage(currentTask.arg); 50 | } 51 | 52 | /** 53 | * Respond to worker successful executions 54 | * 55 | * @private 56 | * @param {Message} message - a web worker message 57 | */ 58 | function onMessage(message) { 59 | if (currentTask) { 60 | currentTask.cb(message.data); 61 | } 62 | currentTask = null; 63 | enqueue(); 64 | } 65 | 66 | /** 67 | * Respond to worker failed executions 68 | * 69 | * @private 70 | * @param {Message} message - a web worker message 71 | * @throws {Error} 72 | */ 73 | function onError(message) { 74 | if (currentTask) { 75 | currentTask.cb(message.data); 76 | } 77 | currentTask = null; 78 | enqueue(); 79 | } 80 | 81 | // expose only the schedule method 82 | this.run = schedule; 83 | }); 84 | -------------------------------------------------------------------------------- /scripts/services/yaml.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | var compose = _.memoize(require('yaml-js/yaml.js').yaml.compose); 6 | 7 | /* 8 | * YAMLWorker bridge and queue 9 | * 10 | */ 11 | var YAMLWorkerBridge = function() { 12 | var YAMLWorker = require('scripts/workers/yaml.worker.js'); 13 | 14 | this.worker = new YAMLWorker(); 15 | this.queue = []; 16 | this.worker.onmessage = this.onmessage.bind(this); 17 | this.worker.onerror = this.onerror.bind(this); 18 | this.buffer = new Map(); 19 | }; 20 | 21 | [ 22 | 'load', 23 | 'loadAll', 24 | 'safeLoad', 25 | 'safeLoadAll', 26 | 'dump', 27 | 'safeDump', 28 | 'scan', 29 | 'parse', 30 | 'compose', 31 | 'compose_all', 32 | 'load_all', 33 | 'emit', 34 | 'serialize', 35 | 'serialize_all', 36 | 'dump_all' 37 | ].forEach(function(method) { 38 | YAMLWorkerBridge.prototype[method] = function(arg, cb) { 39 | this.queue.push({ 40 | method: method, 41 | arg: arg, 42 | cb: cb 43 | }); 44 | this.enqueue(); 45 | }; 46 | }); 47 | 48 | YAMLWorkerBridge.prototype.enqueue = function() { 49 | // if queue is empty do nothing. 50 | if (!this.queue.length) { 51 | this.currentTask = null; 52 | return; 53 | } 54 | 55 | // if there is a currentTask do nothing 56 | if (this.currentTask) { 57 | return; 58 | } 59 | 60 | this.currentTask = this.queue.shift(); 61 | 62 | var task = [this.currentTask.method, this.currentTask.arg]; 63 | var taskString = JSON.stringify(task); 64 | 65 | if (this.buffer.has(taskString)) { 66 | if (this.buffer.get(taskString).error) { 67 | this.currentTask.cb(this.buffer.get(taskString).error); 68 | } else { 69 | this.currentTask.cb(null, this.buffer.get(taskString).result); 70 | } 71 | 72 | this.currentTask = null; 73 | this.enqueue(); 74 | return; 75 | } 76 | 77 | this.worker.postMessage(task); 78 | }; 79 | 80 | YAMLWorkerBridge.prototype.onmessage = function(message) { 81 | var task = [this.currentTask.method, this.currentTask.arg]; 82 | var taskString = JSON.stringify(task); 83 | 84 | if (message.data.error) { 85 | this.buffer.set(taskString, {error: message.data.error}); 86 | this.currentTask.cb(message.data.error); 87 | } else { 88 | this.buffer.set(taskString, {result: message.data.result}); 89 | this.currentTask.cb(null, message.data.result); 90 | } 91 | this.currentTask = null; 92 | this.enqueue(); 93 | }; 94 | 95 | YAMLWorkerBridge.prototype.onerror = function(error) { 96 | this.currentTask.cb(error); 97 | this.currentTask = null; 98 | this.enqueue(); 99 | }; 100 | 101 | /* 102 | * A service for YAMLWorkerBridge to use a single worker for lighter YAML processing 103 | * work 104 | */ 105 | SwaggerEditor.service('YAML', function YAML() { 106 | var worker = new YAMLWorkerBridge(); 107 | 108 | // expose the methods that are being used 109 | this.load = worker.load.bind(worker); 110 | this.dump = worker.dump.bind(worker); 111 | 112 | // Temporarily we are using the main thread to do the composition task due to 113 | // this bug: https://github.com/connec/yaml-js/issues/17 114 | 115 | this.compose = function(string, cb) { 116 | try { 117 | cb(null, compose(string)); 118 | } catch (error) { 119 | cb(error); 120 | } 121 | }; 122 | }); 123 | -------------------------------------------------------------------------------- /scripts/workers/sway.worker.js: -------------------------------------------------------------------------------- 1 | // Worker code from here 2 | /* eslint-env worker */ 3 | onmessage = function(message) { 4 | postMessage({ 5 | errors: [], 6 | specs: message.data.definition, 7 | warnings: [] 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /scripts/workers/yaml.worker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var yaml = require('yaml-js/yaml.js').yaml; 4 | var jsyaml = require('js-yaml'); 5 | 6 | /** 7 | * Worker message listener. 8 | * 9 | * @param {object} message Web Workr message object 10 | * 11 | * # Message format: 12 | * `message` is an array. first argument in the array is the method name string 13 | * and the rest of items are arguments to that method 14 | */ 15 | 16 | /* eslint-env worker */ 17 | onmessage = function onmessage(message) { 18 | if (!Array.isArray(message.data) || message.data.length < 2) { 19 | throw new TypeError('data should be an array with method and arguments'); 20 | } 21 | 22 | var method = message.data[0]; 23 | var args = message.data.slice(1); 24 | var result = null; 25 | var error = null; 26 | var YAML; 27 | 28 | // select YAML engine based on method name 29 | if (method === 'compose_all' || method === 'compose') { 30 | YAML = yaml; 31 | } else { 32 | YAML = jsyaml; 33 | } 34 | 35 | if (typeof YAML[method] !== 'function') { 36 | throw new TypeError('unknown method name'); 37 | } 38 | 39 | try { 40 | result = YAML[method].apply(null, args); 41 | } catch (err) { 42 | error = err; 43 | } 44 | 45 | postMessage({ 46 | result: result, 47 | error: error 48 | }); 49 | }; 50 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var webpack = require('webpack'); 4 | var WebpackDevServer = require('webpack-dev-server'); 5 | var config = require('./webpack.config'); 6 | var open = require('open'); 7 | var IP = '127.0.0.1'; 8 | var argv = require('minimist')(process.argv.slice(2)); 9 | 10 | /** 11 | * Start the server with webpack config 12 | * 13 | * @param {number} port - TCP port 14 | * @param {function} cb - Error first success callback 15 | */ 16 | function startServer(port, cb) { 17 | config.entry.app.unshift('webpack-dev-server/client?http://' + IP + ':' + port + '/'); 18 | 19 | var compiler = webpack(config); 20 | 21 | var server = new WebpackDevServer(compiler, { 22 | progress: true, 23 | stats: 'errors-only', 24 | showModules: false, 25 | publicPath: '/' + config.output.publicPath, 26 | headers: { 27 | 'Set-Cookie': 28 | 'swagger-editor-development-mode:' + 29 | (argv.production ? 'false' : 'true') + ';' 30 | } 31 | }); 32 | 33 | server.listen(port, IP, cb); 34 | } 35 | 36 | // if this file was triggered directly, launch the server 37 | if (require.main === module) { 38 | var PORT = process.env.PORT || 8080; 39 | 40 | startServer(PORT, function(err) { 41 | if (err) { 42 | return console.log(err); 43 | } 44 | 45 | var url = 'http://' + IP + ':' + PORT; 46 | 47 | console.log('Development server started at', url); 48 | 49 | // to avoid opening the browser set DO_NOT_OPEN environment 50 | // variable to true 51 | if (!process.env.DO_NOT_OPEN) { 52 | console.log('Opening the browser'); 53 | open(url); 54 | } 55 | }); 56 | } 57 | 58 | module.exports = startServer; 59 | -------------------------------------------------------------------------------- /spec-files/concourse.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: concourse 3 | 4 | releases: 5 | - name: concourse 6 | version: latest 7 | - name: garden-runc 8 | version: latest 9 | 10 | stemcells: 11 | - alias: trusty 12 | os: ubuntu-trusty 13 | version: latest 14 | 15 | instance_groups: 16 | - name: web 17 | instances: 1 18 | # replace with a VM type from your BOSH Director's cloud config 19 | vm_type: REPLACE_ME 20 | vm_extensions: 21 | # replace with a VM extension from your BOSH Director's cloud config that will attach 22 | # this instance group to your ELB 23 | - REPLACE_ME 24 | stemcell: trusty 25 | azs: [z1] 26 | networks: [{name: private}] 27 | jobs: 28 | - name: atc 29 | release: concourse 30 | properties: 31 | # replace with your CI's externally reachable URL, e.g. https://ci.foo.com 32 | external_url: REPLACE_ME 33 | 34 | # replace with username/password, or configure GitHub auth 35 | basic_auth_username: REPLACE_ME 36 | basic_auth_password: REPLACE_ME 37 | 38 | # replace with your SSL cert and key 39 | tls_cert: REPLACE_ME 40 | tls_key: REPLACE_ME 41 | 42 | postgresql_database: &atc_db atc 43 | - name: tsa 44 | release: concourse 45 | properties: {} 46 | 47 | - name: db 48 | instances: 1 49 | # replace with a VM type from your BOSH Director's cloud config 50 | vm_type: REPLACE_ME 51 | stemcell: trusty 52 | # replace with a disk type from your BOSH Director's cloud config 53 | persistent_disk_type: REPLACE_ME 54 | azs: [z1] 55 | networks: [{name: private}] 56 | jobs: 57 | - name: postgresql 58 | release: concourse 59 | properties: 60 | databases: 61 | - name: *atc_db 62 | # make up a role and password 63 | role: REPLACE_ME 64 | password: REPLACE_ME 65 | 66 | - name: worker 67 | instances: 1 68 | # replace with a VM type from your BOSH Director's cloud config 69 | vm_type: REPLACE_ME 70 | vm_extensions: 71 | # replace with a VM extension from your BOSH Director's cloud config that will attach 72 | # sufficient ephemeral storage to VMs in this instance group. 73 | - REPLACE_ME 74 | stemcell: trusty 75 | azs: [z1] 76 | networks: [{name: private}] 77 | jobs: 78 | - name: groundcrew 79 | release: concourse 80 | properties: {} 81 | - name: baggageclaim 82 | release: concourse 83 | properties: {} 84 | - name: garden 85 | release: garden-runc 86 | properties: 87 | garden: 88 | listen_network: tcp 89 | listen_address: 0.0.0.0:7777 90 | 91 | update: 92 | canaries: 1 93 | max_in_flight: 1 94 | serial: false 95 | canary_watch_time: 1000-60000 96 | update_watch_time: 1000-60000 97 | -------------------------------------------------------------------------------- /spec-files/guide.yml: -------------------------------------------------------------------------------- 1 | # Hit ctrl-space for auto-complete 2 | -------------------------------------------------------------------------------- /spec-files/jmeter-storm-mode.yml: -------------------------------------------------------------------------------- 1 | name: jmeter-storm-deployment 2 | 3 | releases: 4 | - name: jmeter-tornado 5 | version: latest 6 | 7 | stemcells: 8 | - alias: trusty 9 | os: ubuntu-trusty 10 | version: latest 11 | 12 | instance_groups: 13 | - name: workers 14 | instances: 3 15 | vm_type: REPLACE_ME 16 | stemcell: trusty 17 | azs: [z1] 18 | networks: 19 | - name: REPLACE_ME 20 | jobs: 21 | - name: jmeter_storm_worker 22 | release: jmeter-tornado 23 | properties: {} 24 | 25 | - name: storm 26 | instances: 1 27 | lifecycle: errand 28 | vm_type: REPLACE_ME 29 | stemcell: trusty 30 | azs: [z1] 31 | networks: 32 | - name: REPLACE_ME 33 | jobs: 34 | - name: jmeter_storm 35 | release: jmeter-tornado 36 | properties: 37 | wizard: 38 | configuration: 39 | users: 100 40 | ramp_time: 30 41 | duration: 300 42 | targets: 43 | - name: POST with Headers and Body 44 | url: "http://api.example.com:8080/greeting/post/" 45 | http_method: POST 46 | headers: 47 | - name: "Authorization" 48 | value: "Basic Y2F0Om1lb3c=" 49 | options: 50 | request_body: | 51 | {"name" : "i am a post", "age" : 425} 52 | 53 | update: 54 | canaries: 5 55 | max_in_flight: 5 56 | serial: false 57 | canary_watch_time: 1000-60000 58 | update_watch_time: 1000-60000 59 | -------------------------------------------------------------------------------- /spec-files/jmeter-tornado-mode.yml: -------------------------------------------------------------------------------- 1 | name: jmeter-tornado-deployment 2 | 3 | releases: 4 | - name: jmeter-tornado 5 | version: latest 6 | 7 | stemcells: 8 | - alias: trusty 9 | os: ubuntu-trusty 10 | version: latest 11 | 12 | instance_groups: 13 | - name: jmeter_attackers 14 | instances: 3 15 | vm_type: REPLACE_ME 16 | stemcell: trusty 17 | azs: [z1] 18 | networks: 19 | - name: REPLACE_ME 20 | jobs: 21 | - name: jmeter_tornado 22 | release: jmeter-tornado 23 | properties: 24 | wizard: 25 | configuration: 26 | users: 50 27 | ramp_time: 30 28 | targets: 29 | - name: GET with Headers 30 | url: "http://api.example.com:8080/greeting/get/" 31 | http_method: GET 32 | headers: 33 | - name: "Authorization" 34 | value: "Basic Y2F0Om1lb3c=" 35 | 36 | update: 37 | canaries: 5 38 | max_in_flight: 5 39 | serial: false 40 | canary_watch_time: 1000-60000 41 | update_watch_time: 1000-60000 42 | -------------------------------------------------------------------------------- /styles/branding.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Branding CSS file. This file is intentially left blank. If you want to 3 | * customize Swagger Editor you can serve this file with your CSS that overrides 4 | * or extends Swagger Editor styling. 5 | */ 6 | -------------------------------------------------------------------------------- /styles/buttons.less: -------------------------------------------------------------------------------- 1 | @import "colors"; 2 | 3 | @border-radius: 3px; 4 | @button-font-size: 15px; 5 | 6 | .bs-button-override{ 7 | border-radius: 0; 8 | } 9 | 10 | button { 11 | border-radius: 3px; 12 | cursor: pointer; 13 | border: 1px solid @gray-md; 14 | } 15 | 16 | 17 | button.border-only { 18 | background: @white; 19 | 20 | &:hover { 21 | background-color: lighten(@gray-md, 47%); 22 | transition: background-color 0.3s ease-in-out; 23 | } 24 | 25 | &.red:hover { 26 | background: @sw-error-header; 27 | color: @white; 28 | } 29 | } 30 | 31 | button.call { 32 | font-size: 1em; 33 | padding: 0.5em 1em; 34 | display: inline-block; 35 | border:1px solid @gray-lt; 36 | background-color: @gray-lt; 37 | color: @call-button; 38 | border-radius: 3px; 39 | appearance: none; 40 | cursor: pointer; 41 | background-image: (linear-gradient(lighten(@gray-lt, 10%), darken(@gray-lt, 1%))); 42 | 43 | &:hover { 44 | color: @gray-dk; 45 | border: 1px solid @gray-md; 46 | transition: border 0.2s ease-in-out; 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /styles/colors.less: -------------------------------------------------------------------------------- 1 | // UI Colors 2 | @header-bg: #4a4a4a; 3 | @sw-error-header: #FF081C; 4 | @sw-success-header: #90C78F; 5 | @error-text: #FF081C; 6 | @overlay-text: #F8A81C; 7 | @main-splitbar: #5E696B; 8 | @dragover-dash: #138BC4; 9 | @links: #4183c4; 10 | @hr-color: #ddd; 11 | 12 | 13 | // Toolbar 14 | @toolbar-button-color: #808080; 15 | @toolbar-button-bg: #222222; 16 | 17 | // HTTP Colors 18 | @http-get: #2392F7; 19 | @http-post: #13C20F; 20 | @http-delete: #E30012; 21 | @http-patch: #AF01D9; 22 | @http-default: #949493; 23 | @http-put: #FF9000; 24 | @http-options: #BDC3C7; 25 | @http-head: #4AE597; 26 | @http-trace: #BDC3C7; 27 | 28 | // Gray Colors 29 | @gray-lt: #E3E3E3; 30 | @gray-md: #808080; 31 | @gray-dk: #333333; 32 | @gray: #ccc; 33 | 34 | // Colors 35 | @white: white; 36 | @black: black; 37 | @silver: silver; 38 | @red: red; 39 | @green: green; 40 | 41 | // Response Code Colors 42 | @code-default: #555; 43 | @code-200: #13C20F; 44 | @code-300: #0072bc; 45 | @code-400: #f39822; 46 | @code-500: #D40011; 47 | 48 | // Request Response Background Pane 49 | @pre-bg: #fff7e5; 50 | 51 | // Schema Toggle color 52 | @schema-toggle: rgb(66, 90, 66); 53 | 54 | // Security Definitions 55 | @authentication-button: rgb(195, 252, 195); 56 | 57 | // Security 58 | @security-default: #E5E5E5; 59 | @security-basic: #A8E8A1; 60 | @security-apiKey: #A1B2E8; 61 | @security-oauth2: #F4B487; 62 | 63 | // Operation 64 | @background: #fff; 65 | @deprecated-background: #C52A5E; 66 | @table-th: #f7f7f7; 67 | 68 | // Intro 69 | @about-pane: rgba(0, 0, 0, .85); 70 | 71 | // Drop Down Menu 72 | @drop-down-color: rgba(0, 253, 240, 0.81); 73 | 74 | // Modal 75 | @modal-link: rgb(15, 214, 250); 76 | 77 | // Tag Colors 78 | @tag-0: #A60000; 79 | @tag-1: #0022A6; 80 | @tag-2: #24A600; 81 | @tag-3: rgb(255, 223, 0); 82 | @tag-4: #990A5B; 83 | @tag-5: #F27D36; 84 | @tag-6: #32FC34; 85 | @tag-7: #00FFEC; 86 | @tag-8: #CD9EED; 87 | @tag-10: #4FB285; 88 | @tag-11: #0D77CF; 89 | @tag-12: #87EA77; 90 | @tag-13: #EDEDED; 91 | @tag-14: #F560FF; 92 | @tag-15: #E30012; 93 | @tag-border: #EBEBEB; 94 | 95 | // Try This Operation 96 | @try-operation-warning: #FFF645; 97 | 98 | 99 | @editor-error: #AA0A0A; 100 | @external-docs: #337ab7; 101 | @dirty-msg: #FFFF6E; 102 | @call-button: #444; 103 | -------------------------------------------------------------------------------- /styles/components.less: -------------------------------------------------------------------------------- 1 | @import "./components/path"; 2 | @import "./components/operation"; 3 | @import "./components/schema-model"; 4 | @import "./components/try-operation"; 5 | @import "./components/modal"; 6 | @import "./components/path-headers"; 7 | @import "./components/collapse-when"; 8 | @import "./components/error-presenter"; 9 | @import "./components/preview"; 10 | @import "./components/editor"; 11 | @import "./components/header"; 12 | @import "./components/menu-bar"; 13 | @import "./components/intro"; 14 | @import "./components/tags"; 15 | @import "./components/security-definitions"; 16 | @import "./components/tooltip"; 17 | @import "./components/preferences-modal"; 18 | @import "./components/stemcell"; 19 | @import "./components/release"; 20 | @import "./components/variable"; 21 | -------------------------------------------------------------------------------- /styles/components/collapse-when.less: -------------------------------------------------------------------------------- 1 | [collapse-when] { 2 | height: auto; 3 | overflow-y: hidden; 4 | transition: height ease-out 200ms; 5 | 6 | &.c-w-collapsed { 7 | height: 0; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /styles/components/editor.less: -------------------------------------------------------------------------------- 1 | @import "../fonts"; 2 | @import "../colors"; 3 | 4 | .ace-atom-dark{ 5 | .ace_search { 6 | color: @gray-dk; 7 | } 8 | .ace_searchbtn{ 9 | width: 32px; 10 | } 11 | .ace_marker-layer div.ace_selected-word{ 12 | border: 1px solid @white; 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /styles/components/error-presenter.less: -------------------------------------------------------------------------------- 1 | @import "../colors"; 2 | @import "../fonts"; 3 | 4 | .error-presenter { 5 | @warning-color: rgb(218, 132, 71); 6 | .sw-font; 7 | 8 | header { 9 | color: @white; 10 | cursor: pointer; 11 | margin: 0; 12 | 13 | h4 { 14 | display: inline-block; 15 | padding: 0em .5em; 16 | .sw-font-light; 17 | } 18 | 19 | .collapse-button { 20 | float: right; 21 | margin: 8px; 22 | } 23 | } 24 | 25 | &.error { 26 | border: 1px solid @sw-error-header; 27 | 28 | header { 29 | background: @sw-error-header; 30 | } 31 | } 32 | 33 | // TODO: Colors 34 | &.warning { 35 | border: 1px solid @warning-color; 36 | 37 | header { 38 | background: @warning-color; 39 | } 40 | } 41 | 42 | .item { 43 | padding: 10px 10px 10px 25px; 44 | 45 | h5 { 46 | margin-bottom: 0; 47 | .icon { 48 | margin-left: -15px; 49 | } 50 | 51 | &.error { color: darken(@sw-error-header, 20%); } 52 | &.warning { color: @warning-color; } 53 | } 54 | 55 | h6 { 56 | margin: 0; 57 | } 58 | } 59 | 60 | .error-description { 61 | margin: 0; 62 | font-family: monospace; 63 | margin: 0.2em 0; 64 | } 65 | 66 | .details { 67 | h5 { 68 | font-weight: bold; 69 | } 70 | } 71 | 72 | .jump-to-link { 73 | margin: .5em 0; 74 | padding-left: 25px; 75 | margin-left: -25px; 76 | display: block; 77 | background-image: url("../../images/jump-icon.svg"); 78 | background-repeat: no-repeat; 79 | background-position: 0px -3px; 80 | background-size: 0 0; 81 | 82 | &:hover { 83 | background-size: auto auto; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /styles/components/header.less: -------------------------------------------------------------------------------- 1 | @import "../colors"; 2 | 3 | .main-header { 4 | background: @header-bg; 5 | position: fixed; 6 | width: 100%; 7 | height: 30px; 8 | z-index: 5; 9 | } 10 | 11 | a.logo { 12 | background-image: url("../../images/logo.png"); 13 | background-size: contain; 14 | width: 25px; 15 | height: 25px; 16 | margin: 2.5px 3px 0 5px; 17 | float: left; 18 | } 19 | 20 | .menu-bar { 21 | float: left; 22 | } 23 | 24 | .status-bar { 25 | float: right; 26 | padding: 5px; 27 | 28 | .status { 29 | text-align: right; 30 | color: darken(@gray-lt, 25%); 31 | font-weight: 100; 32 | font-size: 12px; 33 | 34 | &.success { 35 | color: @sw-success-header; 36 | } 37 | &.error { 38 | color: @sw-error-header; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /styles/components/intro.less: -------------------------------------------------------------------------------- 1 | @import "../fonts"; 2 | @import "../colors"; 3 | 4 | .about-pane { 5 | position: fixed; 6 | color: @overlay-text; 7 | width: 100%; 8 | height: 100%; 9 | z-index: 2011; 10 | background: @about-pane; 11 | 12 | .modal-header { 13 | position: absolute; 14 | top: 0%; 15 | width: 100%; 16 | text-align: center; 17 | border: none; 18 | } 19 | 20 | h1, h3 { 21 | .sw-font-light; 22 | line-height: 130%; 23 | } 24 | 25 | h1.overlay-title { 26 | font-size: 3em; 27 | } 28 | 29 | .sample { 30 | position: absolute; 31 | top: 10%; 32 | left: 7%; 33 | .sw-font-light; 34 | } 35 | 36 | .file-arrow { 37 | position: absolute; 38 | height: 80px; 39 | width: 80px; 40 | top: 3%; 41 | left: 2%; 42 | background-image: url("../../images/overlay-arrow.svg"); 43 | background-repeat: no-repeat; 44 | background-position: 0px 0px; 45 | background-size: 60px; 46 | } 47 | 48 | .yaml-author { 49 | position: absolute; 50 | top: 40%; 51 | left: 14%; 52 | min-width: 150px; 53 | font-size: 1.4em; 54 | } 55 | 56 | .sw-preview { 57 | position: absolute; 58 | top: 40%; 59 | left: 60%; 60 | min-width: 150px; 61 | font-size: 1.4em; 62 | } 63 | 64 | .btn-intro { 65 | position: absolute; 66 | top: 65%; 67 | left: 44%; 68 | text-align: center; 69 | font-size: 1.3em; 70 | background: none; 71 | border: 1px solid @overlay-text; 72 | color: @overlay-text; 73 | padding: .5em 2em; 74 | letter-spacing: .5px; 75 | } 76 | 77 | .btn-close { 78 | position: absolute; 79 | top: 8px; 80 | right: 14px; 81 | height: 36px; 82 | width: 36px; 83 | color: @overlay-text; 84 | .sw-font-light; 85 | background: none; 86 | border: none; 87 | background-image: url("../../images/close.svg"); 88 | background-repeat: no-repeat; 89 | background-position: 0px 0px; 90 | background-size: 33px; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /styles/components/menu-bar.less: -------------------------------------------------------------------------------- 1 | @import "../colors"; 2 | @import "../fonts"; 3 | @import "../buttons"; 4 | 5 | .column(@num) { 6 | -webkit-column-count: @num; 7 | -moz-column-count: @num; 8 | -ms-column-count: @num; 9 | column-count: @num; 10 | } 11 | 12 | .menu-bar .dropdown{ 13 | display: inline-block; 14 | 15 | button, a { 16 | font-family: 'Open Sans', sans-serif; 17 | font-weight: 100; 18 | font-size: 12px; 19 | background: @header-bg; 20 | user-select: none; 21 | -webkit-user-select: none; 22 | -ms-user-select: none; 23 | color: darken(@white, 15%); 24 | transition: color ease-in 100ms; 25 | } 26 | 27 | &:hover { 28 | button, a { 29 | color: @white; 30 | } 31 | } 32 | 33 | .dropdown-toggle { 34 | border-radius: 0; 35 | background: none; 36 | border: none; 37 | } 38 | 39 | .server-list { 40 | .column(2); 41 | } 42 | 43 | .client-list { 44 | .column(3); 45 | 46 | @media(max-width: 968px) { 47 | .column(2); 48 | } 49 | 50 | @media(max-width: 750px) { 51 | .column(1); 52 | } 53 | } 54 | 55 | .dropdown-menu { 56 | border-radius: 0; 57 | border: none; 58 | box-shadow: none; 59 | min-width: 140px; 60 | 61 | li { 62 | -webkit-column-break-inside: avoid; 63 | -moz-column-break-inside: avoid; 64 | -ms-column-break-inside: avoid; 65 | } 66 | 67 | li a { 68 | overflow: auto; 69 | padding-top: 5px; 70 | padding-bottom: 5px; 71 | 72 | .fa { 73 | font-size: 16px; 74 | margin-right: 5px; 75 | 76 | &.no-icon { 77 | min-width: 13px; 78 | } 79 | 80 | &.highlighted { 81 | color: @drop-down-color; 82 | } 83 | } 84 | } 85 | 86 | .divider { 87 | background: lighten(@header-bg, 30%); 88 | } 89 | 90 | a, button { 91 | 92 | &:hover { 93 | background: darken(@header-bg, 5%); 94 | } 95 | } 96 | } 97 | 98 | &.open { 99 | background: darken(@header-bg, 10%); 100 | 101 | .dropdown-menu, a, li, button, button:focus { 102 | background: darken(@header-bg, 10%); 103 | color: @white; 104 | } 105 | 106 | .dropdown-menu { 107 | margin-top: 0; 108 | 109 | a:hover { 110 | background: darken(@header-bg, 30%); 111 | } 112 | 113 | .divider { 114 | background: darken(@header-bg, 30%); 115 | } 116 | } 117 | } 118 | 119 | .font-size { 120 | span { 121 | background: none; 122 | 123 | &.adjust { 124 | padding: 0px 15px; 125 | border: 1px solid @gray-md; 126 | border-radius: 5px; 127 | margin: 0; 128 | display: inline-table; 129 | 130 | &:hover { 131 | background: darken(@header-bg, 10%); 132 | } 133 | 134 | &:first-child { 135 | border-top-right-radius: 0; 136 | border-bottom-right-radius: 0; 137 | border-right: none; 138 | } 139 | &:last-child { 140 | border-top-left-radius: 0; 141 | border-bottom-left-radius: 0; 142 | } 143 | } 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /styles/components/modal.less: -------------------------------------------------------------------------------- 1 | @import "../buttons"; 2 | @import "../colors"; 3 | @import "../fonts"; 4 | 5 | .modal-dialog .modal-content { 6 | .sw-font; 7 | border-radius: 3px; 8 | background: @gray-dk; 9 | color: @white; 10 | 11 | 12 | .modal-title { 13 | font-size: 1.2em; 14 | } 15 | 16 | .modal-header { 17 | border-bottom: 1px solid @gray-md; 18 | } 19 | 20 | .modal-body { 21 | p { 22 | margin: 1em 0; 23 | } 24 | 25 | .invalid { 26 | color:lighten(@code-500, 10%); 27 | margin-top: .5em; 28 | } 29 | 30 | textarea { 31 | color: @black; 32 | } 33 | 34 | input { 35 | color: @black; 36 | padding: 0.2em; 37 | border-radius: 0; 38 | } 39 | 40 | label { 41 | margin-bottom: .5em; 42 | } 43 | 44 | select { 45 | color: @black; 46 | } 47 | 48 | a { 49 | color: @modal-link; 50 | } 51 | 52 | .error.json-formatter-dark.json-formatter-row span { 53 | white-space: initial; 54 | } 55 | 56 | } 57 | 58 | .modal-footer { 59 | border-top: 1px solid @gray-md; 60 | .btn.btn-primary { 61 | .bs-button-override; 62 | } 63 | .btn.btn-warning { 64 | .bs-button-override; 65 | background-color: @gray-dk; 66 | border: 1px solid @gray-md; 67 | &:hover { 68 | background-color: darken(@gray-lt, 80%); 69 | } 70 | } 71 | } 72 | 73 | .monospace { 74 | font-family: monospace; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /styles/components/path-headers.less: -------------------------------------------------------------------------------- 1 | @import "../colors"; 2 | @import "../fonts"; 3 | 4 | .info :not([marked]) { 5 | margin-bottom: 1em; 6 | 7 | .info-container { 8 | padding: 0 10px; 9 | transition: height ease-in .3s; 10 | 11 | &.collapsed { 12 | height: 0 !important; 13 | overflow: hidden; 14 | } 15 | } 16 | 17 | .external-docs { 18 | margin-top: 1em; 19 | a { color: @external-docs; } 20 | } 21 | 22 | h1 { 23 | font-size: 1.2em; 24 | .sw-font-light; 25 | margin-top: 0px; 26 | 27 | &:hover { 28 | cursor: pointer; 29 | } 30 | } 31 | 32 | p { 33 | width: 100%; 34 | .sw-font-light; 35 | line-height: 1.3em; 36 | margin-bottom: 1em; 37 | } 38 | 39 | h4 { 40 | margin-bottom: 0; 41 | font-size: 1em; 42 | .sw-font-med; 43 | 44 | &:last-child { 45 | margin-bottom: 0; 46 | } 47 | > span { 48 | .sw-font-light; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /styles/components/path.less: -------------------------------------------------------------------------------- 1 | @import "../colors"; 2 | @import "../fonts"; 3 | 4 | ul.paths { 5 | list-style-type: none; 6 | padding: 0; 7 | } 8 | 9 | li.path { 10 | list-style-type: none; 11 | padding-bottom: 1.2em; 12 | margin-left: 14px; 13 | 14 | >header { 15 | position: relative; 16 | cursor: pointer; 17 | 18 | h2 { 19 | .sw-font; 20 | font-family: Source Code Pro; 21 | display: inline-block; 22 | font-size: 1.2em; 23 | padding: 0; 24 | margin-bottom: 5px; 25 | margin-top: 3px; 26 | 27 | a { 28 | color: @gray-dk; 29 | text-decoration: none; 30 | 31 | &:hover { 32 | cursor: pointer; 33 | border-bottom: 1px dotted @silver; 34 | 35 | } 36 | } 37 | } 38 | } 39 | 40 | .jump-to-yaml { 41 | position: absolute; 42 | top: 0; 43 | right: 0; 44 | padding: 0.3em; 45 | user-select: none; 46 | font-size: 0.8em; 47 | display: inline-block; 48 | height: 16px; 49 | width: 22px; 50 | color: @gray-md; 51 | background-image: url("../../images/jump-icon.svg"); 52 | background-repeat: no-repeat; 53 | background-position: 0px -3px; 54 | background-size: 22px; 55 | 56 | &:hover { 57 | color: @gray-dk; 58 | transition: all 0.2s ease-in-out; 59 | cursor: pointer; 60 | text-decoration: none; 61 | } 62 | 63 | } 64 | 65 | ul.operations { 66 | 67 | &.collapsed { 68 | display: none; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /styles/components/preferences-modal.less: -------------------------------------------------------------------------------- 1 | .preferences-modal { 2 | .keypress-debounce { 3 | max-width: 200px; 4 | } 5 | 6 | .preferences-form { 7 | label { 8 | display: block; 9 | } 10 | 11 | p { 12 | font-weight: normal; 13 | font-size: 12px; 14 | } 15 | 16 | tr:not(:first-child) th { 17 | padding-top: 20px; 18 | } 19 | 20 | th:last-child { 21 | padding-left: 30px; 22 | min-width: 150px; 23 | vertical-align: middle; 24 | 25 | input[type="checkbox"] { 26 | font-size: 20px; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /styles/components/preview.less: -------------------------------------------------------------------------------- 1 | @import "../colors"; 2 | @import "../fonts"; 3 | 4 | .preview.pane { 5 | .sw-font; 6 | 7 | .dirty-message { 8 | background: @dirty-msg; 9 | padding: .3em; 10 | } 11 | 12 | .section-header { 13 | a { 14 | cursor: pointer; 15 | color: @gray-dk; 16 | 17 | &:hover { 18 | text-decoration: none; 19 | border-bottom: 1px dotted @silver; 20 | } 21 | } 22 | 23 | .on-hover { 24 | display: none; 25 | font-size: 14px; 26 | } 27 | 28 | .toggle { 29 | font-size: 14px; 30 | } 31 | 32 | &:hover .on-hover { 33 | display: inline-block; 34 | } 35 | } 36 | 37 | .section-content { 38 | margin-left: 10px; 39 | list-style: none; 40 | padding-left: 0; 41 | } 42 | 43 | .definition { 44 | h4 { 45 | .jump-to-yaml { 46 | float: right; 47 | position: relative; 48 | top: -4px; 49 | right: 0; 50 | padding: 0.3em; 51 | -webkit-user-select: none; 52 | -moz-user-select: none; 53 | -ms-user-select: none; 54 | user-select: none; 55 | font-size: 0.8em; 56 | display: inline-block; 57 | height: 19px; 58 | width: 22px; 59 | color: @gray-md; 60 | background-image: url("../../images/jump-icon.svg"); 61 | } 62 | } 63 | 64 | } 65 | 66 | .definition-title { 67 | color: @gray-dk; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /styles/components/release.less: -------------------------------------------------------------------------------- 1 | .release-label { 2 | font-size: 90%; 3 | } 4 | -------------------------------------------------------------------------------- /styles/components/schema-model.less: -------------------------------------------------------------------------------- 1 | @import "../colors"; 2 | @import "../fonts"; 3 | 4 | .schema-model { 5 | 6 | &.hidden { 7 | display: none; 8 | } 9 | 10 | a.toggle { 11 | color: @schema-toggle; 12 | text-decoration: none; 13 | margin: 0 0.3em; 14 | padding: 0 0.2em; 15 | border-radius: 3px; 16 | 17 | &:hover { 18 | background: @schema-toggle; 19 | color: @white; 20 | } 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /styles/components/security-definitions.less: -------------------------------------------------------------------------------- 1 | @import "../colors"; 2 | 3 | .security-definition-theme(@color) { 4 | border: 1px solid @color; 5 | >header { 6 | background: @color; 7 | } 8 | } 9 | 10 | .security-definitions { 11 | .security-definition { 12 | 13 | .security-definition-theme(@security-default); 14 | &.basic { .security-definition-theme(@security-basic); } 15 | &.apiKey { .security-definition-theme(@security-apiKey); } 16 | &.oauth2 { .security-definition-theme(@security-oauth2); } 17 | 18 | >header, >section { padding: .4em; } 19 | margin-bottom: 1em; 20 | 21 | .security-type { 22 | font-size: .7em; 23 | } 24 | 25 | .authentication { 26 | float: right; 27 | 28 | button { 29 | background: @authentication-button; 30 | border: none; 31 | padding: 0.1em 0.5em 32 | } 33 | } 34 | } 35 | 36 | .scopes-table td { 37 | padding-right: 1em; 38 | } 39 | 40 | tbody tr td { 41 | border-width: 0; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /styles/components/stemcell.less: -------------------------------------------------------------------------------- 1 | .stemcell-label { 2 | font-size: 90%; 3 | } 4 | -------------------------------------------------------------------------------- /styles/components/tags.less: -------------------------------------------------------------------------------- 1 | .tags { 2 | text-align: right; 3 | &.root { 4 | text-align: left; 5 | } 6 | .tag { 7 | border-radius: 3px; 8 | padding: 2px 10px; 9 | margin: 2px; 10 | color: @white; 11 | background: @gray-md; 12 | display: inline-block; 13 | text-decoration: none; 14 | .delete-tag { 15 | color: inherit; 16 | display: inline-block; 17 | padding: 0 10px; 18 | text-decoration: none; 19 | margin-right: -10px; 20 | &:hover { 21 | color: @red; 22 | } 23 | } 24 | } 25 | .tag-color-0 { background: @tag-0; } 26 | .tag-color-1 { background: @tag-1; } 27 | .tag-color-2 { background: @tag-2; } 28 | .tag-color-3 { background: @tag-3; color: @black; } 29 | .tag-color-4 { background: @tag-4; } 30 | .tag-color-5 { background: @tag-5; } 31 | .tag-color-6 { background: @tag-6; color: @black; } 32 | .tag-color-7 { background: @tag-7; color: @black; } 33 | .tag-color-8 { background: @tag-8; color: @black; } 34 | .tag-color-9 { background: @black; } 35 | .tag-color-10 { background: @tag-10; } 36 | .tag-color-11 { background: @tag-11; } 37 | .tag-color-12 { background: @tag-12; color: @black; } 38 | .tag-color-13 { background: @tag-13; color: @black; } 39 | .tag-color-14 { background: @tag-14; } 40 | .tag-color-15 { background: @tag-15; } 41 | } 42 | 43 | .all-tags { 44 | table { 45 | width: 100%; 46 | } 47 | tr:not(:last-child) { 48 | border-bottom: 1px solid @tag-border; 49 | } 50 | td { 51 | padding: 10px; 52 | vertical-align: top; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /styles/components/tooltip.less: -------------------------------------------------------------------------------- 1 | .tooltip { 2 | .tooltip-inner { 3 | border-radius: 0; 4 | } 5 | &.left { 6 | transform: translateY(5px); 7 | .tooltip-arrow { 8 | right: 1px; 9 | top: 40%; 10 | } 11 | > * { 12 | white-space: pre; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /styles/components/try-operation.less: -------------------------------------------------------------------------------- 1 | @import "../colors"; 2 | @import "../fonts"; 3 | @import "../buttons"; 4 | 5 | .operation-button(@color) { 6 | background: @color; 7 | } 8 | .try-operation { 9 | position: relative; 10 | 11 | .json-body { 12 | border: none; 13 | margin: 0; 14 | padding: 0; 15 | } 16 | 17 | .response-info { 18 | padding: .5em; 19 | margin: .5em 0; 20 | background: lighten(@green, 50%); 21 | 22 | &.error { 23 | background: lighten(@red, 40%); 24 | } 25 | } 26 | 27 | .options-table { 28 | td { 29 | padding: .25em 1em; 30 | } 31 | } 32 | 33 | .raw-model { 34 | min-height: 300px; 35 | } 36 | 37 | .headers { 38 | margin: 1em 0; 39 | } 40 | 41 | .cors-warning { 42 | background-color: @try-operation-warning; 43 | padding: 0.6em .4em; 44 | line-height: 1.5; 45 | margin: 1em 0; 46 | p {margin: 0;} 47 | code { 48 | background: none; 49 | } 50 | } 51 | 52 | .call[disabled] { 53 | cursor: not-allowed; 54 | } 55 | 56 | // json schema form adjustment (Hack) 57 | [data-schemaid="root"] { 58 | > h3 .btn-group { 59 | display: none; // hide [Edit JSON] and [Object Properties] in root 60 | } 61 | 62 | > .well > div > div > .row > div > h3 >.btn-group { 63 | display: none; // hide buttons in [Parameters] hash 64 | } 65 | } 66 | 67 | >div { 68 | >h5 { 69 | font-size: 1.0em; 70 | .sw-font-med; 71 | 72 | &:first-child { 73 | margin-top: 25px; 74 | } 75 | } 76 | 77 | >div { 78 | padding: .5em 0 1em; 79 | } 80 | 81 | .raw { 82 | width: 100%; 83 | padding: .4em; 84 | background: @pre-bg; 85 | font-family: monospace; 86 | border: none; 87 | margin: 1em 0; 88 | word-wrap: break-word; 89 | } 90 | 91 | .parameters { 92 | 93 | .required-icon { 94 | color: @sw-error-header; 95 | } 96 | 97 | table.edit-params { 98 | width: 100%; 99 | 100 | td { 101 | vertical-align: top; 102 | 103 | 104 | } 105 | 106 | .param-name { 107 | text-align: right; 108 | padding: 0.3em 0.6em; 109 | font-size: .9em; 110 | } 111 | 112 | textarea, input { 113 | -webkit-appearance: none; 114 | border: 1px solid lighten(@gray-md, 20%); 115 | border-radius: 2px; 116 | display: block; 117 | width: 100%; 118 | padding: .4em .6em 119 | } 120 | } 121 | } 122 | 123 | .request { 124 | .raw-request { 125 | } 126 | .form-control { 127 | color: @gray-dk !important; 128 | } 129 | } 130 | 131 | .response { 132 | .failed-bar { 133 | .sw-font-light; 134 | background: @sw-error-header; 135 | color: @white; 136 | padding: .3em; 137 | } 138 | .raw-response { 139 | white-space: pre; 140 | } 141 | 142 | .responses-tabs .tab-content { 143 | max-height: 300px; 144 | overflow: auto; 145 | } 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /styles/components/variable.less: -------------------------------------------------------------------------------- 1 | .variable-label { 2 | font-size: 90%; 3 | } 4 | 5 | .variable-password { 6 | background-color: #d9534f; 7 | } 8 | 9 | .variable-certificate { 10 | background-color: #5cb85c; 11 | } 12 | 13 | .variable-ssh { 14 | background-color: #5bc0de; 15 | } 16 | 17 | .variable-rsa { 18 | background-color: #f0ad4e;; 19 | } 20 | -------------------------------------------------------------------------------- /styles/fonts.less: -------------------------------------------------------------------------------- 1 | .pre-font { 2 | font-family: 'Source Code Pro', monospace; 3 | } 4 | 5 | .sw-font { 6 | font-family: "Open Sans"; 7 | font-weight: 400; 8 | } 9 | 10 | .sw-font-heavy { 11 | .sw-font; 12 | font-weight: 700; 13 | } 14 | 15 | .sw-font-med { 16 | .sw-font; 17 | font-weight: 600; 18 | } 19 | 20 | .sw-font-light { 21 | .sw-font; 22 | font-weight: 300; 23 | } 24 | -------------------------------------------------------------------------------- /styles/main.less: -------------------------------------------------------------------------------- 1 | // packages imported from node_modules are prefixed with ~ 2 | @import "~angular-ui-layout/src/ui-layout.css"; 3 | @import "~jsonformatter/dist/json-formatter.css"; 4 | @import "~json-schema-view-js/dist/style.css"; 5 | @import "~bootstrap/less/bootstrap.less"; 6 | @import "~font-awesome/less/font-awesome.less"; 7 | @import "~source-code-pro/source-code-pro.css"; 8 | @import "~open-sans-fontface/open-sans.less"; 9 | 10 | @import "./fonts"; 11 | @import "./buttons"; 12 | @import "./components"; 13 | @import "./markdown"; 14 | @import "./print"; 15 | @import "./colors"; 16 | 17 | @splitter-width: 4px; 18 | 19 | html, body { 20 | height: 100%; 21 | font-family: sans-serif; 22 | } 23 | 24 | /* 25 | Layout 26 | */ 27 | main { 28 | position: fixed; 29 | top: 30px; 30 | bottom: 0; 31 | right: 0; 32 | left: 0; 33 | 34 | .ui-layout-column .ui-splitbar { 35 | background: @main-splitbar; 36 | 37 | /* IE10 fixes*/ 38 | display: -ms-flexbox; 39 | -ms-flex-pack: center; 40 | -ms-flex-direction: column; 41 | } 42 | 43 | .pane.editor { 44 | >div, .editor-wrapper { 45 | height: 100%; 46 | } 47 | } 48 | 49 | &.preview-wrapper, .preview-wrapper { 50 | overflow: auto; 51 | margin: .5em 1em; 52 | } 53 | 54 | &.dragover { 55 | .file-drag { 56 | display: flex; 57 | } 58 | } 59 | 60 | .file-drag { 61 | display: none; 62 | position: fixed; 63 | top: 0; 64 | bottom: 0; 65 | left: 0; 66 | right: 0; 67 | background: lightgray; 68 | z-index: 10000; 69 | align-items: center; 70 | 71 | .dash { 72 | border: 10px dashed @dragover-dash; 73 | border-radius: 30px; 74 | width: 60%; 75 | height: 40%; 76 | margin: auto; 77 | display: flex; 78 | 79 | p { 80 | margin: auto; 81 | font-size: 50px; 82 | font-weight: 100; 83 | } 84 | } 85 | } 86 | } 87 | 88 | /* 89 | Reset 90 | */ 91 | a { 92 | cursor: pointer; 93 | } 94 | -------------------------------------------------------------------------------- /styles/print.less: -------------------------------------------------------------------------------- 1 | /* 2 | * We only care about printing in preview mode. Result of printing in other 3 | * modes is not important. 4 | */ 5 | @media print { 6 | html, body { 7 | height: 100%; 8 | } 9 | 10 | // List of elements that should not be rendered for printer. 11 | // This list includes elements that user interact with it. 12 | .main-header, 13 | .ui-splitbar, 14 | .editor.pane, 15 | .root.tags { 16 | display: none; 17 | } 18 | 19 | .preview.pane, 20 | main, 21 | main > div { 22 | width: 100% !important; 23 | top: 0; 24 | position: static !important; 25 | } 26 | 27 | li.operation { 28 | page-break-inside: avoid; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /templates/about.html: -------------------------------------------------------------------------------- 1 | 4 | 22 | 25 | -------------------------------------------------------------------------------- /templates/addons-tags.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

5 | 6 | 7 | Tags 8 | 9 |

10 |
11 | 12 |
    16 | 17 |
  • 22 | {{key}}: {{value}} 23 |
  • 24 |
25 | -------------------------------------------------------------------------------- /templates/addons.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

5 | 6 | 7 | Addons ({{specs.addons.length}}) 8 | 9 |

10 |
11 | 12 |
    16 |
  • 19 | 20 |
    24 |
    {{addon.name}} 27 |
    28 |
    29 | 30 |
    31 |

    Description

    32 |
    33 |
    34 | 35 |
    39 | 40 |
    41 |

    Include:

    42 |
    43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
    DeploymentsJobsStemcell(OS)
    {{d}}{{$last ? '' : ', '}}{{j.name}}{{$last ? '' : ', '}}{{addon.include.stemcell.os}}
    59 |
    60 |
    61 | 62 |
    63 |

    Jobs

    64 |
    65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
    NameRelease
    {{job.name}}{{job.release}}
    79 |
    80 |
    81 |
    82 |
  • 83 |
84 | -------------------------------------------------------------------------------- /templates/choose-mode.html: -------------------------------------------------------------------------------- 1 | 4 | 8 | 12 | -------------------------------------------------------------------------------- /templates/error-presenter.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

5 | 6 | Warning{{errorsAndWarnings.length > 1 ? 's' : ''}} 7 | 8 | 9 | Error{{errorsAndWarnings.length > 1 ? 's' : ''}} 10 | 11 |

12 | {{isCollapsed ? 'Open' : 'Collapse'}} 13 |
14 | 15 |
16 |
19 | 20 |
21 | {{isWarning(error) ? '⚠' : '✖' }} 22 | {{error.type}} 23 |
24 | 25 |

26 | 30 | Jump to line {{error.lineNumber}} 31 | 32 | 33 |
34 |
Details
35 | 36 |
37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /templates/file-import.html: -------------------------------------------------------------------------------- 1 | 4 | 9 | 13 | -------------------------------------------------------------------------------- /templates/import.html: -------------------------------------------------------------------------------- 1 | 4 | 9 | 13 | -------------------------------------------------------------------------------- /templates/instance-groups.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |

6 | 7 | 8 | Instance Groups ({{specs.instance_groups.length}}) 9 | 10 | 11 | 12 | Toggle 13 | 14 |

15 |
16 | 17 |
    21 |
  • 24 | 25 |
    29 |
    {{instanceGroup.name}} 32 |
    33 |
    34 | 35 |
    36 |

    Description

    37 |
    38 |
    39 | 40 |
    44 |
    45 | 48 | {{instanceGroup.lifecycle || "service"}} 49 | 50 |
    51 | 52 |
    53 |

    Info

    54 |
    55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
    InstancesVM TypeStemcellAZs
    {{instanceGroup.instances}}{{instanceGroup.vm_type}}{{instanceGroup.stemcell}}{{az}}{{$last ? '' : ', '}}
    73 |
    74 |
    75 | 76 |
    77 |

    Jobs

    78 |
    79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 |
    NameRelease
    {{job.name}}{{job.release}}
    93 |
    94 |
    95 |
    96 |
  • 97 |
98 | -------------------------------------------------------------------------------- /templates/intro.html: -------------------------------------------------------------------------------- 1 |
2 | 6 |
7 |
Try a sample here
8 | 10 |
Write manifests in YAML...
11 |
...and enjoy dynamic auto-complete features
12 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /templates/open-examples.html: -------------------------------------------------------------------------------- 1 | 4 | 8 | 12 | -------------------------------------------------------------------------------- /templates/path.html: -------------------------------------------------------------------------------- 1 |
4 | 5 |

6 | {{pathName}} 7 |

8 | 9 | 15 | 16 |
17 | 18 |
    19 | 22 | 23 |
24 | 25 | -------------------------------------------------------------------------------- /templates/preferences.html: -------------------------------------------------------------------------------- 1 | 4 | 50 | 54 | -------------------------------------------------------------------------------- /templates/releases.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |

6 | 7 | 8 | Releases ({{specs.releases.length}}) 9 | 10 |

11 |
12 |
    16 |
  • 21 | {{release.version}} 22 | {{release.name}} 23 |
  • 24 |
25 | -------------------------------------------------------------------------------- /templates/reset-editor.html: -------------------------------------------------------------------------------- 1 | 4 | 8 | 12 | -------------------------------------------------------------------------------- /templates/schema-model.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 |
4 | 8 | 9 | ⇄ 10 | 11 |
16 | 17 | -------------------------------------------------------------------------------- /templates/specs-info.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | Deployment: {{specs.name}} 5 |

6 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /templates/stemcells.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |

6 | 7 | 8 | Stemcells ({{specs.stemcells.length}}) 9 | 10 |

11 |
12 | 13 |
    17 |
  • 22 | {{stemcell.version}} 23 | Alias: {{stemcell.alias}} 24 | 25 | Name: {{stemcell.name}} 26 | 27 | 28 | OS: {{stemcell.os}} 29 | 30 |
  • 31 |
32 | -------------------------------------------------------------------------------- /templates/update-block.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |

6 | 7 | 8 | Update Block 9 | 10 |

11 |
12 | 13 |
    17 |
  • 18 | canaries: {{specs.update.canaries}} 19 |
  • 20 |
  • 21 | max_in_flight: {{specs.update.max_in_flight}} 22 |
  • 23 |
  • 24 | canary_watch_time: {{specs.update.canary_watch_time}} 25 |
  • 26 |
  • 27 | update_watch_time: {{specs.update.update_watch_time}} 28 |
  • 29 |
  • 30 | serial: {{specs.update.serial}} 31 |
  • 32 |
33 | -------------------------------------------------------------------------------- /templates/url-import.html: -------------------------------------------------------------------------------- 1 | 4 | 22 | 26 | -------------------------------------------------------------------------------- /templates/variables.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |

6 | 7 | Variables ({{specs.variables.length}}) 8 | 9 | 10 | Toggle 11 | 12 |

13 |
14 | 15 |
    19 |
  • 23 | 24 |
    27 | 28 | 29 | 31 | {{variable.type}} 32 | 33 | 34 |
    37 | {{variable.name}} 38 | {{variable.usages.length}} 39 |
    40 |
    41 | 42 |
    46 | 47 |
    48 |

    Options

    49 |
    {{variable.prettyOptions}}
    50 |
    51 | 52 |
    53 |

    Used: {{variable.usages.length}} times

    54 |
    55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    {{item}}
    63 |
    64 |
    65 |
    66 |
  • 67 |
68 | -------------------------------------------------------------------------------- /test/.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: '../.eslintrc.js', 5 | globals: { 6 | sinon: false, 7 | inject: false 8 | }, 9 | env: { 10 | jasmine: true, 11 | mocha: true, 12 | }, 13 | plugins: [ 14 | 'chai-expect' 15 | ], 16 | rules: { 17 | 'chai-expect/missing-assertion': 2, 18 | 'chai-expect/terminating-properties': 1, 19 | 'no-unused-expressions': 0 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /test/e2e/.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: '../.eslintrc.js', 5 | globals: { 6 | $: false, 7 | browser: false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/e2e/protractor.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var PORT = process.env.TEST_PORT || 8282; 4 | var path = require('path'); 5 | var createServer = require('http-server').createServer; 6 | var server = null; 7 | var root = path.join(__dirname, '../..'); 8 | 9 | var config = { 10 | beforeLaunch: function() { 11 | server = createServer({ 12 | root: root 13 | }); 14 | 15 | server.listen(PORT, function(err) { 16 | if (err) { 17 | return console.error(err); 18 | } 19 | 20 | console.log('Test server started at http://localhost:' + PORT); 21 | }); 22 | }, 23 | 24 | baseUrl: 'http://localhost:' + PORT + '/', 25 | 26 | directConnect: true, 27 | 28 | capabilities: { 29 | browserName: 'chrome', 30 | chromeOptions: { 31 | args: ['--test-type'] 32 | } 33 | }, 34 | 35 | // To test specific files you can limit the spec files to steps 1, 2 and the 36 | // step you are looking for. For example: 37 | // 38 | // specs: [ 39 | // 'specs/**/1*test.js', 40 | // 'specs/**/2*test.js', 41 | // 'specs/**/5*test.js' 42 | // ], 43 | // 44 | specs: ['specs/**/*test.js'], 45 | 46 | jasmineNodeOpts: { 47 | showColors: true, 48 | isVerbose: true, 49 | realtimeFailure: true, 50 | includeStackTrace: true, 51 | defaultTimeoutInterval: 30000 52 | }, 53 | 54 | allScriptsTimeout: 50000 55 | }; 56 | 57 | exports.config = config; 58 | -------------------------------------------------------------------------------- /test/e2e/specs/1_console_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * This test file just opens the web app and examine if 5 | * there is any console errors 6 | * It marks tests failed if there is any errors in console 7 | */ 8 | 9 | describe('Console tests', function() { 10 | it('should load the app', function() { 11 | browser.get('/'); 12 | expect(browser.getTitle()).toContain('Swagger Editor'); 13 | }); 14 | 15 | it('Should not have any console errors or warnings', function() { 16 | browser.manage().logs().get('browser').then(function(browserLog) { 17 | var errorLogs = browserLog.filter(function(log) { 18 | return log.level.value > 900; 19 | }); 20 | if (errorLogs.length) { 21 | console.log('\n\nBrowser console log: \n\n'); 22 | errorLogs.forEach(console.log); 23 | } 24 | expect(errorLogs.length).toBe(0); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/e2e/specs/2_navigation_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * Test navigation behavior 5 | */ 6 | 7 | var version = require('../../../package.json').version; 8 | 9 | describe('Navigation', function() { 10 | it('should show the intro', function() { 11 | expect($('.about-pane').isPresent()).toBe(true); 12 | }); 13 | 14 | it('should be able to dismiss the intro', function() { 15 | $('#dismis-intro').click(); 16 | 17 | expect($('.about-pane').isPresent()).toBe(false); 18 | }); 19 | 20 | it('should open the modal', function() { 21 | $('.help.dropdown button').click(); 22 | $('.help.dropdown ul li:nth-child(3) a').click(); 23 | 24 | expect($('.modal-content').isPresent()).toBe(true); 25 | }); 26 | 27 | it('should show correct version number in the about modal', function() { 28 | expect($('.modal-body .version-number').getText()).toContain(version); 29 | }); 30 | 31 | it('should close the modal', function() { 32 | $('.modal-footer .btn').click(); 33 | 34 | expect($('.modal-content').isPresent()).toBe(false); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/e2e/specs/3_rendering_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * Test rendering behavior 5 | */ 6 | 7 | describe('Rendering', function() { 8 | var elements = [ 9 | ['Main Header', '.main-header'], 10 | ['File Menu', '[ng-if="showFileMenu()"]'], 11 | ['Preview Pane', '.preview-wrapper'], 12 | ['Info Container', '.info-container'], 13 | ['Paths', '.preview-wrapper ul.paths'], 14 | ['At least a path', '.path'], 15 | ['At least an operation', '.operation'], 16 | ['At least a parameter', '.params'], 17 | ['Models', '[ng-if="specs.definitions"]'], 18 | ['At least a model', '.schema-model'] 19 | ]; 20 | 21 | var expectElement = function(name, selector) { 22 | it('should render ' + name, function() { 23 | expect($(selector).isPresent()).toBe(true); 24 | }); 25 | }; 26 | elements.forEach(function(element) { 27 | expectElement(element[0], element[1]); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/e2e/specs/4_errors_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * Test errors that is being presented to user 5 | */ 6 | 7 | var setValue = function(value) { 8 | browser.executeScript(function(value) { 9 | document.querySelector('[ui-ace]').env.editor.setValue(value); 10 | }, value); 11 | browser.sleep(500); 12 | }; 13 | 14 | describe('Error Presenter', function() { 15 | it('should show an error when document is empty', function() { 16 | setValue(''); 17 | expect($('.error-presenter').isPresent()).toBe(true); 18 | }); 19 | 20 | it('should show YAML syntax error with invalid YAML', function() { 21 | setValue('invalid:1\n yaml:'); 22 | 23 | expect($('.error-presenter').isPresent()).toBe(true); 24 | expect($('.error-header h4').getText()).toContain('Error'); 25 | expect($('.error-presenter .item h5.error').getText()) 26 | .toContain('YAML Syntax Error'); 27 | }); 28 | 29 | it('should show Swagger Error with invalid swagger', function() { 30 | var val = [ 31 | 'swagger: "2.0"', 32 | 'info:', 33 | ' version: "1.0.0"', 34 | ' title: Petstore', 35 | 'paths:', 36 | ' /users:', 37 | ' post:', 38 | ' responses:', 39 | ' 200: {}' 40 | ].join('\n'); 41 | 42 | setValue(val); 43 | 44 | expect($('.error-presenter').isPresent()).toBe(true); 45 | expect($('.error-header h4').getText()).toContain('Error'); 46 | expect($('.error-presenter .item h5.error').getText()) 47 | .toContain('Swagger Error'); 48 | }); 49 | 50 | it('should show swagger warning with a document that has warnings', 51 | function() { 52 | var val = [ 53 | 'swagger: "2.0"', 54 | 'info:', 55 | ' version: "1.0.0"', 56 | ' title: Petstore', 57 | 'paths:', 58 | ' /users:', 59 | ' post:', 60 | ' responses:', 61 | ' 200:', 62 | ' description: OK', 63 | 'definitions:', 64 | ' User: {}' 65 | ].join('\n'); 66 | 67 | setValue(val); 68 | 69 | expect($('.error-presenter').isPresent()).toBe(true); 70 | expect($('.error-header h4').getText()).toContain('Warning'); 71 | } 72 | ); 73 | }); 74 | -------------------------------------------------------------------------------- /test/e2e/specs/5_session_auth_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * This test file just opens the web app and examine if 5 | * there is store the security map 6 | * It marks tests success if there is 7 | */ 8 | 9 | var fs = require('fs'); 10 | var path = require('path'); 11 | var yamlPath = path.join(__dirname, './session.yaml'); 12 | var swyaml = fs.readFileSync(yamlPath).toString(); 13 | 14 | var setValue = function(value) { 15 | browser.executeScript(function(value) { 16 | document.querySelector('[ui-ace]').env.editor.setValue(value); 17 | }, value); 18 | browser.sleep(1000); 19 | }; 20 | 21 | describe('Session auth tests', function() { 22 | beforeEach(function() { 23 | browser.executeAsyncScript(function(done) { 24 | window.sessionStorage.clear(); 25 | done(); 26 | }); 27 | }); 28 | 29 | // Fix tests (to do) 30 | it('Should find the sessionStorage', function() { 31 | // swyaml is the test yaml file 32 | setValue(swyaml); 33 | browser.executeScript(function() { 34 | return JSON.parse( 35 | window.sessionStorage.getItem('ngStorage-securityKeys') 36 | ); 37 | }).then(function(storeAuth) { 38 | expect(storeAuth.hasOwnProperty('githubAccessCode')).toEqual(true); 39 | expect(storeAuth.hasOwnProperty('petstoreImplicit')).toEqual(true); 40 | expect(storeAuth.hasOwnProperty('internalApiKey')).toEqual(true); 41 | expect(storeAuth.hasOwnProperty('anynotfound')).toEqual(false); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/e2e/specs/6_examples_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Example', function() { 4 | [ 5 | 'Uber API', 6 | 'PetStore on Heroku', 7 | 'Simple API', 8 | 'Echo', 9 | 'Swagger Petstore (Simple)', 10 | 'Swagger Petstore', 11 | 'Basic Auth Example', 12 | 'Swagger Sample API' 13 | ].forEach(testExample); 14 | }); 15 | 16 | /** 17 | * @param {string} title - title 18 | * @param {int} index - index 19 | */ 20 | function testExample(title, index) { 21 | describe(title, function() { 22 | it('should open ' + title, function() { 23 | $('#fileMenu').click(); 24 | $('#open-example').click(); 25 | 26 | // sleep for modal to finish animation. Protractor should do this, but for 27 | // now it's not doing it so we put this sleep here 28 | browser.sleep(100); 29 | $('.modal-dialog select').click(); 30 | $('.modal-dialog select option:nth-child(' + (index + 1) + ')').click(); 31 | $('.modal-dialog .btn.btn-primary').click(); 32 | 33 | browser.wait(function() { 34 | return $('.modal-dialog').isPresent().then(function(isPresent) { 35 | return !isPresent; 36 | }); 37 | }, 5000); 38 | 39 | expect($('.modal-dialog').isPresent()).toBe(false); 40 | }); 41 | 42 | it('should show the info box for ' + title, function() { 43 | expect($('.info-header').getText()).toContain(title); 44 | }); 45 | 46 | it('should show no errors for ' + title, function() { 47 | expect($('.error-presenter').isPresent()).toBe(false); 48 | }); 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /test/e2e/specs/7_try_operation_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Try Operation', function() { 4 | it('opens "Heroku Pets" example', function() { 5 | $('#fileMenu').click(); 6 | $('#open-example').click(); 7 | $('.modal-dialog select').click(); 8 | 9 | // heroku pets is second one 10 | $('.modal-dialog select option:nth-child(2)').click(); 11 | $('.modal-dialog .btn.btn-primary').click(); 12 | 13 | browser.sleep(300); // wait for modal to go away 14 | 15 | expect($('.modal-dialog').isPresent()).toBe(false); 16 | 17 | browser.wait(function() { 18 | return $('.info-header').getText().then(function(text) { 19 | return text.indexOf('PetStore on Heroku') > -1; 20 | }); 21 | }, 5000); 22 | expect($('.info-header').getText()).toContain('PetStore on Heroku'); 23 | }); 24 | 25 | it('should show no errors', function() { 26 | expect($('.error-presenter').isPresent()).toBe(false); 27 | }); 28 | 29 | it('opens try this operation for GET /', function() { 30 | $('ul.paths > li:nth-child(1) li.get.operation .try-operation > button') 31 | .click(); 32 | expect($('.try-container').isPresent()).toBe(true); 33 | }); 34 | 35 | it('renders the form for "limit" parameter', function() { 36 | expect( 37 | $('.try-container input[name="root[parameters][limit]"]').isPresent() 38 | ).toBe(true); 39 | }); 40 | 41 | it('renders correct request URL', function() { 42 | expect($('.try-container .raw-request .line.url a').getText()) 43 | .toContain('http://petstore-api.herokuapp.com/pet/?limit=11'); 44 | }); 45 | 46 | it('changing the scheme changes the request URL', function() { 47 | $('.try-container select[name="root[scheme]"]').click(); 48 | $('.try-container select[name="root[scheme]"] option:nth-child(2)').click(); 49 | 50 | expect($('.try-container .raw-request .line.url a').getText()) 51 | .toContain('https://petstore-api.herokuapp.com'); 52 | }); 53 | 54 | it('updating the limit value updates request URL', function() { 55 | var limitInput = $('.try-container input[name="root[parameters][limit]"]'); 56 | 57 | limitInput.clear(); 58 | limitInput.sendKeys('20'); 59 | 60 | // to release focus from the input and trigger the value change 61 | $('.main-header').click(); 62 | 63 | expect($('.try-container .raw-request .line.url a').getText()) 64 | .toContain('limit=20'); 65 | }); 66 | 67 | it('sends the request and response appears', function() { 68 | $('button[ng-click="makeCall()"]').click(); 69 | 70 | // wait for the XHR call 71 | browser.wait(function() { 72 | return $('.try-container .response-info').isPresent(); 73 | }, 20000); // 20 seconds. it takes a long time for the Heroku app to wake up 74 | 75 | // renders the headers 76 | expect($('[json="responseHeaders"]').isPresent()).toBe(true); 77 | 78 | // renders the body 79 | expect($('[json="responseData"]').isPresent()).toBe(true); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /test/e2e/specs/session.yaml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | info: 3 | version: 1.0.9-abcd 4 | title: Swagger Sample API 5 | basePath: /v1 6 | schemes: 7 | - http 8 | - https 9 | security: 10 | - githubAccessCode: 11 | - user 12 | - user:email 13 | - petstoreImplicit: 14 | - user 15 | - user:email 16 | - internalApiKey: [] 17 | paths: 18 | /pets/{id}: 19 | get: 20 | parameters: 21 | - name: id 22 | in: path 23 | description: ID of pet to use 24 | required: true 25 | type: array 26 | items: 27 | type: string 28 | collectionFormat: csv 29 | description: Returns pets based on ID 30 | summary: Find pets by ID 31 | operationId: getPetsById 32 | security: 33 | - githubAccessCode: 34 | - user 35 | - internalApiKey: [] 36 | responses: 37 | default: 38 | description: error payload 39 | schema: 40 | $ref: '#/definitions/ErrorModel' 41 | securityDefinitions: 42 | githubAccessCode: 43 | type: oauth2 44 | scopes: 45 | user: Grants read/write . 46 | user:email: Grants read . 47 | flow: accessCode 48 | authorizationUrl: https://github.com/login/oauth/authorize 49 | tokenUrl: https://github.com/login/oauth/access_token 50 | petstoreImplicit: 51 | type: oauth2 52 | scopes: 53 | user: Grants read/write . 54 | user:email: Grants read . 55 | flow: implicit 56 | authorizationUrl: http://domain.com/oauth/dialog 57 | internalApiKey: 58 | type: apiKey 59 | in: header 60 | name: api_key 61 | definitions: 62 | ErrorModel: 63 | required: 64 | - code 65 | - message 66 | properties: 67 | code: 68 | type: integer 69 | format: int32 70 | message: 71 | type: string 72 | -------------------------------------------------------------------------------- /test/unit/bootstrap.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Test Bootstrap 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var angular = require('angular'); 8 | 9 | angular.bootstrap(window.document, ['SwaggerEditor']); 10 | -------------------------------------------------------------------------------- /test/unit/defaults.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Test defaults 3 | */ 4 | 5 | 'use strict'; 6 | 7 | SwaggerEditor.config(function($provide) { 8 | $provide.constant('defaults', { 9 | disableCodeGen: true, 10 | examplesFolder: 'spec-files/', 11 | editorOptions: {}, 12 | exampleFiles: [ 13 | 'default.yaml', 14 | 'heroku-pets.yaml', 15 | 'minimal.yaml', 16 | 'echo.yaml', 17 | 'petstore_simple.yaml', 18 | 'petstore_full.yaml', 19 | 'basic-auth.yaml', 20 | 'security.yaml' 21 | ], 22 | autocompleteExtension: {}, 23 | useBackendForStorage: false, 24 | backendEndpoint: '/editor/spec', 25 | useYamlBackend: false, 26 | disableFileMenu: false, 27 | headerBranding: false, 28 | enableTryIt: true, 29 | brandingCssClass: '', 30 | importProxyUrl: 'https://cors-it.herokuapp.com/?url=' 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | require('angular-json-schema-form'); 5 | require('json-editor'); 6 | require('ngstorage'); 7 | require('angular-ui-ace'); 8 | 9 | // Define window.SwaggerEditor to have angular-mocks dependency 10 | window.SwaggerEditor = angular.module('SwaggerEditor', [ 11 | require('angular-mocks/ngMock'), 12 | require('angular-sanitize'), 13 | require('ng-file-upload'), 14 | require('angular-ui-router'), 15 | require('angular-ui-bootstrap'), 16 | require('angular-ui-layout'), 17 | require('angular-marked'), 18 | require('jsonformatter'), 19 | 'ui.ace', 20 | 'mohsen1.schema-form', 21 | 'ngStorage' 22 | ]); 23 | 24 | // Require app components 25 | require('scripts/components.js'); 26 | 27 | // Test config 28 | require('defaults.js'); 29 | 30 | // Load tests 31 | require('spec/controllers/editor'); 32 | require('spec/controllers/errorpresenter'); 33 | require('spec/controllers/file-import'); 34 | require('spec/controllers/main'); 35 | require('spec/controllers/openexamples'); 36 | require('spec/controllers/preview'); 37 | require('spec/controllers/tryoperation/hasRequestBody'); 38 | require('spec/controllers/tryoperation/getRequestBody'); 39 | require('spec/controllers/tryoperation/isCrossOrigin'); 40 | require('spec/controllers/tryoperation/isType'); 41 | require('spec/controllers/tryoperation/isJson'); 42 | require('spec/controllers/tryoperation/makeRequestModel'); 43 | require('spec/controllers/tryoperation/generateUrl'); 44 | require('spec/controllers/tryoperation/getHeaders'); 45 | require('spec/controllers/tryoperation/makeCall'); 46 | require('spec/controllers/url-import'); 47 | 48 | require('spec/directives/collapsewhen'); 49 | require('spec/directives/schemamodel'); 50 | 51 | require('spec/services/ast-manager'); 52 | require('spec/services/autocomplete'); 53 | require('spec/services/backend'); 54 | require('spec/services/builder'); 55 | require('spec/services/editor'); 56 | require('spec/services/fileloader'); 57 | require('spec/services/fold-state-manager'); 58 | require('spec/services/json-schema'); 59 | require('spec/services/storage'); 60 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | 4 | var path = require("path"); 5 | 6 | var webpackConfig = require('../../webpack.config.js'); 7 | 8 | module.exports = function(config) { 9 | config.set({ 10 | 11 | // Define a proxy to allow worker files served at correct path 12 | proxies: {}, 13 | 14 | // base path, that will be used to resolve files and exclude 15 | basePath: '.', 16 | 17 | // testing framework to use (jasmine/mocha/qunit/...) 18 | frameworks: ['mocha', 'sinon-chai', 'chai', 'chai-as-promised'], 19 | 20 | // list of files / patterns to load in the browser 21 | files: [ 22 | 'index.js' 23 | ], 24 | 25 | // list of files / patterns to exclude 26 | exclude: [], 27 | 28 | // Process test fies with webpack so require statements work in them 29 | preprocessors: { 30 | 'index.js': ['webpack', 'sourcemap'] 31 | }, 32 | 33 | // Use a better looking test reporter 34 | reporters: ['mocha'], 35 | 36 | // enable webpack 37 | webpack: { 38 | devtool: 'inline-source-map', 39 | module: { 40 | loaders: webpackConfig.module.loaders 41 | }, 42 | resolve: { 43 | root: [ 44 | __dirname, 45 | path.join(__dirname, '../../') 46 | ] 47 | } 48 | }, 49 | 50 | // Configuarion of webpackMiddleware 51 | webpackMiddleware: { 52 | quiet: true, 53 | progress: true, 54 | noInfo: true 55 | }, 56 | 57 | // web server port 58 | port: 8080, 59 | 60 | // level of logging 61 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || 62 | // LOG_INFO || LOG_DEBUG 63 | logLevel: config.LOG_WARN, 64 | 65 | // enable / disable watching file and executing tests whenever any file 66 | // changes 67 | autoWatch: true, 68 | 69 | // Start these browsers, currently available: 70 | // - Chrome 71 | // - ChromeCanary 72 | // - Firefox 73 | // - Opera 74 | // - Safari (only Mac) 75 | // - PhantomJS 76 | // - IE (only Windows) 77 | browsers: ['Chrome'], 78 | 79 | // Continuous Integration mode 80 | // if true, it capture browsers, run tests and exit 81 | singleRun: false 82 | }); 83 | }; 84 | -------------------------------------------------------------------------------- /test/unit/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Unit Test Runner 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/editor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Controller: EditorCtrl', function() { 6 | // load the controller's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | var scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | $controller('EditorCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should have a scope', function() { 20 | expect(Boolean(scope)).to.equal(true); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/errorpresenter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Controller: ErrorPresenterCtrl', function() { 6 | // load the controller's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | var scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | var rootScope = $rootScope.$new(); 15 | rootScope.warnings = []; 16 | rootScope.errors = []; 17 | 18 | $controller('ErrorPresenterCtrl', { 19 | $scope: scope, 20 | $rootScope: rootScope 21 | }); 22 | })); 23 | 24 | it('should have a scope', function() { 25 | expect(Boolean(scope)).to.equal(true); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/file-import.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Controller: FileImportCtrl', function() { 6 | // load the controller's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | var modalInstance; 10 | var scope; 11 | 12 | // Initialize the controller and a mock scope 13 | beforeEach(inject(function($controller, $rootScope) { 14 | scope = $rootScope.$new(); 15 | var callback = sinon.spy(); 16 | modalInstance = { // Create a mock object using spies 17 | close: callback, 18 | dismiss: callback, 19 | result: { 20 | then: callback 21 | } 22 | }; 23 | $controller('FileImportCtrl', { 24 | $scope: scope, 25 | $uibModalInstance: modalInstance 26 | }); 27 | })); 28 | 29 | it('should have a scope', function() { 30 | expect(Boolean(scope)).to.equal(true); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Controller: MainCtrl', function() { 6 | // load the controller's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | var scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | $controller('MainCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should have a scope', function() { 20 | expect(Boolean(scope)).to.equal(true); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/openexamples.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Controller: OpenExamplesCtrl', function() { 6 | // load the controller's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | var FileLoader; 10 | var $uibModalInstance; 11 | var scope; 12 | 13 | // Initialize the controller and a mock scope 14 | beforeEach(inject(function($controller, $rootScope) { 15 | scope = $rootScope.$new(); 16 | 17 | // Create a mock object using spies 18 | var callback = sinon.spy(); 19 | $uibModalInstance = { 20 | close: callback, 21 | dismiss: callback, 22 | result: { 23 | then: callback 24 | } 25 | }; 26 | 27 | FileLoader = { 28 | loadFromUrl: function() { 29 | return { 30 | then: function() {} 31 | }; 32 | } 33 | }; 34 | 35 | $controller('OpenExamplesCtrl', { 36 | $scope: scope, 37 | $uibModalInstance: $uibModalInstance, 38 | FileLoader: FileLoader 39 | }); 40 | })); 41 | 42 | it('should have a scope', function() { 43 | expect(Boolean(scope)).to.equal(true); 44 | }); 45 | 46 | it('should select first example as selected file by default', function() { 47 | expect(scope.selectedFile).to.equal('default.yaml'); 48 | }); 49 | 50 | it('honors defaults.examplesFolder configuration when opening files', 51 | function() { 52 | sinon.stub(FileLoader, 'loadFromUrl').returns({then: sinon.spy()}); 53 | 54 | scope.open('aFile'); 55 | 56 | expect(FileLoader.loadFromUrl).to.have.been.calledWithMatch('aFile'); 57 | } 58 | ); 59 | }); 60 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/preview.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Controller: PreviewCtrl', function() { 6 | // load the controller's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | var scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | $controller('PreviewCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should have a scope', function() { 20 | expect(Boolean(scope)).to.equal(true); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/tryoperation/getHeaders.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: TryOperation', function() { 4 | beforeEach(window.module('SwaggerEditor')); 5 | 6 | var $controller; 7 | var scope; 8 | 9 | beforeEach(inject(function(_$controller_, $rootScope) { 10 | scope = $rootScope.$new(); 11 | $controller = _$controller_; 12 | scope.operation = {}; 13 | scope.specs = {}; 14 | scope.getParameters = function mockGetParameters() { 15 | return []; 16 | }; 17 | scope.$watch = function() {}; 18 | $controller('TryOperation', { 19 | $scope: scope 20 | }); 21 | })); 22 | 23 | describe('$scope.getHeaders', function() { 24 | describe('Basic Swagger', function() { 25 | beforeEach(function() { 26 | scope.specs = { 27 | swagger: '2.0', 28 | info: { 29 | version: '0.0.0', 30 | title: 'Simple API' 31 | }, 32 | paths: { 33 | '/': { 34 | get: { 35 | responses: { 36 | 200: { 37 | description: 'OK' 38 | } 39 | } 40 | } 41 | } 42 | } 43 | }; 44 | scope.operation = { 45 | responses: { 46 | 200: { 47 | description: 'OK' 48 | } 49 | } 50 | }; 51 | scope.pathName = '/'; 52 | }); 53 | }); 54 | 55 | it('returns header parameters', function() { 56 | scope.getHeaderParams = {}; 57 | scope.specs.host = '127.0.0.1:8080'; 58 | $controller('TryOperation', { 59 | $scope: scope 60 | }); 61 | 62 | var params = { 63 | 'Host': '127.0.0.1', 64 | 'Accept': '*/*', 65 | 'Accept-Encoding': 'gzip,deflate,sdch', 66 | 'Accept-Language': 'en-US,en;q=0.8,fa;q=0.6,sv;q=0.4', 67 | 'Cache-Control': 'no-cache', 68 | 'Connection': 'keep-alive', 69 | 'Origin': 'http://localhost:8080', 70 | 'Referer': 'http://localhost:8080/context.html', 71 | 'User-Agent': window.navigator.userAgent 72 | }; 73 | var headerParams = scope.getHeaders(); 74 | 75 | expect(headerParams).to.deep.equal(params); 76 | }); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/tryoperation/getRequestBody.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: TryOperation', function() { 4 | beforeEach(window.module('SwaggerEditor')); 5 | 6 | var $controller; 7 | var scope; 8 | 9 | beforeEach(inject(function(_$controller_, $rootScope) { 10 | scope = $rootScope.$new(); 11 | $controller = _$controller_; 12 | scope.operation = {}; 13 | scope.specs = {}; 14 | scope.getParameters = function mockGetParameters() { 15 | return []; 16 | }; 17 | scope.$watch = function() {}; 18 | $controller('TryOperation', { 19 | $scope: scope 20 | }); 21 | })); 22 | 23 | describe('$scope.getRequestBody', function() { 24 | beforeEach(function() { 25 | var parameters = [ 26 | { 27 | name: "body", 28 | in: "body", 29 | description: "description", 30 | schema: { 31 | type: "object", 32 | properties: { 33 | foo: { 34 | type: "string" 35 | }, 36 | bar: { 37 | type: "string", 38 | enum: [ 39 | "bar", 40 | "baz" 41 | ] 42 | } 43 | } 44 | } 45 | } 46 | ]; 47 | scope.specs = { 48 | swagger: "2.0", 49 | info: { 50 | version: "0.0.0", 51 | title: "Simple API" 52 | }, 53 | path: { 54 | "/": { 55 | get: { 56 | parameters: parameters, 57 | responses: { 58 | 200: { 59 | description: "OK" 60 | } 61 | } 62 | } 63 | } 64 | } 65 | }; 66 | scope.getParameters = function() { 67 | return parameters; 68 | }; 69 | $controller('TryOperation', { 70 | $scope: scope 71 | }); 72 | }); 73 | 74 | it('should return null if there is no body-model', function() { 75 | scope.requestModel = { 76 | scheme: "http", 77 | accept: "*/*", 78 | contentType: "application/json", 79 | parameters: { 80 | body: null 81 | } 82 | }; 83 | var requestBody = scope.getRequestBody(); 84 | expect(requestBody).to.equal(null); 85 | }); 86 | 87 | it('should return correct body model', function() { 88 | scope.requestModel = { 89 | scheme: "http", 90 | accept: "*/*", 91 | contentType: "application/json", 92 | parameters: { 93 | body: { 94 | foo: "foo", 95 | bar: "bar" 96 | } 97 | } 98 | }; 99 | var bodyParam = {foo: "foo", bar: "bar"}; 100 | var requestBody = scope.getRequestBody(); 101 | expect(requestBody).to.equal(JSON.stringify(bodyParam, null, 2)); 102 | }); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/tryoperation/hasRequestBody.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: TryOperation', function() { 4 | beforeEach(window.module('SwaggerEditor')); 5 | 6 | var $controller; 7 | var scope; 8 | 9 | beforeEach(inject(function(_$controller_, $rootScope) { 10 | scope = $rootScope.$new(); 11 | $controller = _$controller_; 12 | scope.operation = {}; 13 | scope.specs = {}; 14 | scope.getParameters = function mockGetParameters() { 15 | return []; 16 | }; 17 | scope.$watch = function() {}; 18 | $controller('TryOperation', { 19 | $scope: scope 20 | }); 21 | })); 22 | 23 | describe('$scope.hasRequestBody', function() { 24 | beforeEach(function() { 25 | var parameters = [ 26 | { 27 | name: "body", 28 | in: "body", 29 | description: "description", 30 | schema: { 31 | type: "object", 32 | properties: { 33 | foo: { 34 | type: "string" 35 | }, 36 | bar: { 37 | type: "string", 38 | enum: [ 39 | "bar", 40 | "baz" 41 | ] 42 | } 43 | } 44 | } 45 | } 46 | ]; 47 | scope.specs = { 48 | swagger: "2.0", 49 | info: { 50 | version: "0.0.0", 51 | title: "Simple API" 52 | }, 53 | path: { 54 | "/": { 55 | get: { 56 | parameters: parameters, 57 | responses: { 58 | 200: { 59 | description: "OK" 60 | } 61 | } 62 | } 63 | } 64 | } 65 | }; 66 | scope.getParameters = function() { 67 | return parameters; 68 | }; 69 | $controller('TryOperation', { 70 | $scope: scope 71 | }); 72 | }); 73 | 74 | it('should return 1 for one body parameter', function() { 75 | var hasRequestBody = scope.hasRequestBody(); 76 | expect(hasRequestBody).to.be.equal(1); 77 | }); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/tryoperation/isCrossOrigin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: TryOperation', function() { 4 | beforeEach(window.module('SwaggerEditor')); 5 | 6 | var $controller; 7 | var scope; 8 | 9 | beforeEach(inject(function(_$controller_, $rootScope) { 10 | scope = $rootScope.$new(); 11 | $controller = _$controller_; 12 | scope.operation = {}; 13 | scope.specs = {}; 14 | scope.getParameters = function mockGetParameters() { 15 | return []; 16 | }; 17 | scope.$watch = function() {}; 18 | $controller('TryOperation', { 19 | $scope: scope 20 | }); 21 | })); 22 | 23 | describe('scope.isCrossOrigin', function() { 24 | beforeEach(function() { 25 | scope.locationHost = 'localhost'; 26 | }); 27 | 28 | it('is a function', function() { 29 | expect(scope.isCrossOrigin).to.be.a.function; 30 | }); 31 | 32 | it('returns true if swagger host is not equal to window.location.host', 33 | function() { 34 | scope.specs = {host: 'example.com'}; 35 | expect(scope.isCrossOrigin()).to.equal(true); 36 | }); 37 | 38 | it('returns flase if swagger host is equal to window.location.host', 39 | function() { 40 | scope.specs = {host: 'localhost'}; 41 | expect(scope.isCrossOrigin()).to.equal(false); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/tryoperation/isJson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: TryOperation', function() { 4 | beforeEach(window.module('SwaggerEditor')); 5 | 6 | var $controller; 7 | var scope; 8 | 9 | beforeEach(inject(function(_$controller_, $rootScope) { 10 | scope = $rootScope.$new(); 11 | $controller = _$controller_; 12 | scope.operation = {}; 13 | scope.specs = {}; 14 | scope.getParameters = function mockGetParameters() { 15 | return []; 16 | }; 17 | scope.$watch = function() {}; 18 | $controller('TryOperation', { 19 | $scope: scope 20 | }); 21 | })); 22 | 23 | describe('scope.isJson', function() { 24 | it('is a function', function() { 25 | expect(scope.isJson).to.be.a.function; 26 | }); 27 | 28 | it('returns true for objects', function() { 29 | var obj = {}; 30 | expect(scope.isJson(obj)).to.equal(true); 31 | }); 32 | 33 | it('returns true for arrays', function() { 34 | var arr = []; 35 | expect(scope.isJson(arr)).to.equal(true); 36 | }); 37 | 38 | it('returns true for JSON string', function() { 39 | var obj = "{}"; 40 | var arr = "[]"; 41 | 42 | expect(scope.isJson(obj)).to.equal(true); 43 | expect(scope.isJson(arr)).to.equal(true); 44 | }); 45 | 46 | it('returns false for non-JSON string', function() { 47 | var str = "string"; 48 | 49 | expect(scope.isJson(str)).to.equal(false); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/tryoperation/isType.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: TryOperation', function() { 4 | beforeEach(window.module('SwaggerEditor')); 5 | 6 | var $controller; 7 | var scope; 8 | 9 | beforeEach(inject(function(_$controller_, $rootScope) { 10 | scope = $rootScope.$new(); 11 | $controller = _$controller_; 12 | scope.operation = {}; 13 | scope.specs = {}; 14 | scope.getParameters = function mockGetParameters() { 15 | return []; 16 | }; 17 | scope.$watch = function() {}; 18 | $controller('TryOperation', { 19 | $scope: scope 20 | }); 21 | })); 22 | 23 | describe('scope.isType', function() { 24 | it('is a function', function() { 25 | expect(scope.isType).to.be.a.function; 26 | }); 27 | 28 | it('returns false for html type and image Content-Type', function() { 29 | var header = {"Content-Type": "image/jpeg"}; 30 | var type = "html"; 31 | 32 | expect(scope.isType(header, type)).to.equal(false); 33 | }); 34 | 35 | it('returns true for html type and text Content-Type', function() { 36 | expect(scope.isType({"Content-Type": "text/html"}, "html")) 37 | .to.equal(true); 38 | }); 39 | 40 | it('returns true for json type and json Content-Type', function() { 41 | expect(scope.isType({"Content-Type": "application/json"}, "json")) 42 | .to.equal(true); 43 | }); 44 | 45 | it('returns false for text type and json Content-Type', function() { 46 | expect(scope.isType({"Content-Type": "application/json"}, "text")) 47 | .to.equal(false); 48 | }); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/tryoperation/makeCall.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var $ = require('jquery'); 4 | 5 | describe('Controller: TryOperation', function() { 6 | beforeEach(window.module('SwaggerEditor')); 7 | 8 | var $controller; 9 | var scope; 10 | 11 | beforeEach(inject(function(_$controller_, $rootScope) { 12 | scope = $rootScope.$new(); 13 | $controller = _$controller_; 14 | })); 15 | 16 | describe('$scope.makeCall', function() { 17 | it('should call ajax when it\'s called', function() { 18 | var operation = { 19 | responses: { 20 | 200: { 21 | description: 'OK' 22 | } 23 | } 24 | }; 25 | scope.specs = { 26 | swagger: '2.0', 27 | info: { 28 | version: '0.0.0', 29 | title: 'Simple API' 30 | }, 31 | paths: { 32 | '/': { 33 | get: operation 34 | } 35 | } 36 | }; 37 | scope.pathName = '/'; 38 | scope.operation = operation; 39 | scope.getParameters = function mockGetParameters() { 40 | return []; 41 | }; 42 | scope.$watch = function() {}; 43 | $controller('TryOperation', { 44 | $scope: scope 45 | }); 46 | 47 | var ajaxStub = sinon.stub($, 'ajax').returns({ 48 | fail: function() { 49 | return {done: function() {}}; 50 | } 51 | }); 52 | 53 | scope.makeCall(); 54 | 55 | ajaxStub.should.have.been.called; 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/tryoperation/makeRequestModel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: TryOperation', function() { 4 | beforeEach(window.module('SwaggerEditor')); 5 | 6 | var $controller; 7 | var scope; 8 | 9 | beforeEach(inject(function(_$controller_, $rootScope) { 10 | scope = $rootScope.$new(); 11 | $controller = _$controller_; 12 | scope.operation = {}; 13 | scope.specs = {}; 14 | scope.getParameters = function mockGetParameters() { 15 | return []; 16 | }; 17 | scope.$watch = function() {}; 18 | $controller('TryOperation', { 19 | $scope: scope 20 | }); 21 | })); 22 | 23 | describe('$scope.makeRequestModel', function() { 24 | describe('Basic Swagger', function() { 25 | beforeEach(function() { 26 | scope.specs = { 27 | swagger: '2.0', 28 | info: { 29 | version: '0.0.0', 30 | title: 'Simple API' 31 | }, 32 | paths: { 33 | '/': { 34 | get: { 35 | responses: { 36 | 200: { 37 | description: 'OK' 38 | } 39 | } 40 | } 41 | } 42 | } 43 | }; 44 | scope.operation = { 45 | parameters: {}, 46 | responses: { 47 | 200: { 48 | description: 'OK' 49 | } 50 | } 51 | }; 52 | scope.pathName = '/'; 53 | }); 54 | }); 55 | 56 | it('should return the model', function() { 57 | var objModel = {scheme: 'http', accept: '*/*'}; 58 | var model = scope.requestModel; 59 | expect(model).to.deep.equal(objModel); 60 | }); 61 | 62 | it('model should contain parameters', function() { 63 | var parameters = [ 64 | { 65 | name: 'body', 66 | in: 'body', 67 | description: 'description', 68 | schema: { 69 | type: 'object', 70 | properties: { 71 | foo: { 72 | type: 'string' 73 | } 74 | } 75 | } 76 | } 77 | ]; 78 | scope.getParameters = function() { 79 | return parameters; 80 | }; 81 | $controller('TryOperation', { 82 | $scope: scope 83 | }); 84 | var objModel = { 85 | scheme: 'http', 86 | accept: '*/*', 87 | contentType: 'application/json', 88 | parameters: { 89 | body: {foo: null} 90 | } 91 | }; 92 | var model = scope.requestModel; 93 | expect(model).to.deep.equal(objModel); 94 | }); 95 | 96 | it('should prefill request parameter if it has a default value', 97 | function() { 98 | var parameters = [ 99 | { 100 | name: "body", 101 | in: "body", 102 | description: "description", 103 | schema: { 104 | type: "object", 105 | properties: { 106 | foo: { 107 | type: "string", 108 | default: "BLAH" 109 | } 110 | } 111 | } 112 | } 113 | ]; 114 | scope.specs = { 115 | swagger: '2.0', 116 | info: { 117 | version: '0.0.0', 118 | title: 'Simple API' 119 | }, 120 | paths: { 121 | '/': { 122 | post: { 123 | summary: "boo", 124 | description: "boo", 125 | parameters: parameters, 126 | responses: { 127 | 200: { 128 | description: "OK" 129 | } 130 | } 131 | } 132 | } 133 | } 134 | }; 135 | scope.getParameters = function() { 136 | return parameters; 137 | }; 138 | $controller('TryOperation', { 139 | $scope: scope 140 | }); 141 | var objModel = { 142 | scheme: 'http', 143 | accept: '*/*', 144 | contentType: 'application/json', 145 | parameters: { 146 | body: {foo: "BLAH"} 147 | } 148 | }; 149 | var model = scope.requestModel; 150 | expect(model).to.deep.equal(objModel); 151 | }); 152 | }); 153 | }); 154 | -------------------------------------------------------------------------------- /test/unit/spec/controllers/url-import.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Controller: UrlImportCtrl', function() { 6 | // load the controller's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | var $uibModalInstance; 10 | var scope; 11 | 12 | // Initialize the controller and a mock scope 13 | beforeEach(inject(function($controller, $rootScope) { 14 | scope = $rootScope.$new(); 15 | 16 | // Create a mock object using spies 17 | var callback = sinon.spy(); 18 | $uibModalInstance = { 19 | close: callback, 20 | dismiss: callback, 21 | result: { 22 | then: callback 23 | } 24 | }; 25 | $controller('UrlImportCtrl', { 26 | $scope: scope, 27 | $uibModalInstance: $uibModalInstance 28 | }); 29 | })); 30 | 31 | it('should have a scope', function() { 32 | expect(Boolean(scope)).to.equal(true); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/unit/spec/directives/collapsewhen.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Directive: collapseWhen', function() { 6 | // load the directive's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | var element; 10 | var scope; 11 | 12 | beforeEach(inject(function($rootScope) { 13 | scope = $rootScope.$new(); 14 | })); 15 | 16 | it('should make hidden element visible', inject(function($compile) { 17 | element = angular.element( 18 | '
this is the collapseWhen directive
'); 19 | element = $compile(element)(scope); 20 | expect(element.text()).to.equal('this is the collapseWhen directive'); 21 | })); 22 | }); 23 | -------------------------------------------------------------------------------- /test/unit/spec/directives/schemamodel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Directive: schemaModel', function() { 6 | // load the directive's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | var element; 10 | var scope; 11 | 12 | beforeEach(inject(function($rootScope) { 13 | scope = $rootScope.$new(); 14 | })); 15 | 16 | it('should render', inject(function($compile) { 17 | scope.stringSchema = {type: 'string'}; 18 | element = angular.element( 19 | '' 20 | ); 21 | element = $compile(element)(scope); 22 | expect(element.text()).to.contain('⇄'); 23 | })); 24 | }); 25 | -------------------------------------------------------------------------------- /test/unit/spec/services/autocomplete.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Service: Autocomplete', function() { 6 | // load the service's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | // instantiate service and capture getCompletions method 10 | var rootScope; 11 | var getCompletions; 12 | var editor = { 13 | completer: {autoSelect: false} 14 | }; 15 | var session = {}; 16 | var simpleYaml = [ 17 | // 0 1 2 3 18 | // 012345678901234567890123457890 19 | 'swagger: "2.0"', // 0 20 | '', // 1 21 | 'info:', // 2 22 | ' ', // 3 23 | ' version: 0.0.0', // 4 24 | ' title: Simple API', // 5 25 | ' ', // 6 26 | 'paths:', // 7 27 | ' ', // 8 28 | ' /:', // 9 29 | ' get:', // 10 30 | ' ', // 11 31 | ' responses:', // 12 32 | ' "200":', // 13 33 | ' ', // 14 34 | ' description: OK', // 15 35 | ' ', // 16 36 | '' // 17 37 | ].join('\n'); 38 | var getValue = function(item) { 39 | return item.value; 40 | }; 41 | 42 | beforeEach(inject(function($rootScope, _Autocomplete_) { 43 | rootScope = $rootScope; 44 | _Autocomplete_.init(editor); 45 | getCompletions = editor.completers[0].getCompletions; 46 | })); 47 | 48 | describe('root level suggestions', function() { 49 | it('suggests root keywords when "s" is typed', function(done) { 50 | var position = {row: 0, column: 0}; 51 | var prefix = 's'; 52 | 53 | rootScope.editorValue = simpleYaml; 54 | 55 | getCompletions(editor, session, position, prefix, function(e, list) { 56 | var values = list.map(getValue); 57 | 58 | expect(values).to.contain('swagger'); 59 | expect(values).to.contain('info'); 60 | expect(values).to.contain('paths'); 61 | expect(values).not.to.contain('title'); 62 | expect(values).not.to.contain('version'); 63 | done(e); 64 | }); 65 | }); 66 | }); 67 | 68 | describe('second level suggestions', function() { 69 | it('suggests "contact" if cursor on top of "info" hash', function(done) { 70 | var prefix = 'c'; 71 | rootScope.editorValue = simpleYaml; 72 | 73 | var position = {row: 3, column: 3}; 74 | 75 | getCompletions(editor, session, position, prefix, function(e, list) { 76 | var values = list.map(getValue); 77 | 78 | try { 79 | expect(values).to.contain('contact'); 80 | } catch (err) { 81 | return done(err); 82 | } 83 | 84 | done(e); 85 | }); 86 | }); 87 | 88 | it('suggests "contact" if cursor in bottom of "info"', function(done) { 89 | var prefix = 'c'; 90 | rootScope.editorValue = simpleYaml; 91 | 92 | var position = {row: 6, column: 3}; 93 | 94 | getCompletions(editor, session, position, prefix, function(e, list) { 95 | var values = list.map(getValue); 96 | 97 | try { 98 | expect(values).to.contain('contact'); 99 | } catch (err) { 100 | return done(err); 101 | } 102 | 103 | done(e); 104 | }); 105 | }); 106 | }); 107 | 108 | describe('enum suggestions', function() { 109 | it('suggests "2.0" as a value for "swagger" key', function(done) { 110 | var position = {row: 0, column: 10}; 111 | var prefix = '"2'; 112 | 113 | rootScope.editorValue = 'swagger: 2'; 114 | 115 | getCompletions(editor, session, position, prefix, function(e, list) { 116 | var values = list.map(getValue); 117 | 118 | try { 119 | expect(values).to.contain('"2.0"'); 120 | } catch (err) { 121 | return done(err); 122 | } 123 | 124 | done(e); 125 | }); 126 | }); 127 | }); 128 | }); 129 | -------------------------------------------------------------------------------- /test/unit/spec/services/backend.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Service: Backend', function() { 6 | // load the service's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | // instantiate service 10 | var Backend; 11 | beforeEach(inject(function(_Backend_) { 12 | Backend = _Backend_; 13 | })); 14 | 15 | it('should exists', function() { 16 | expect(Boolean(Backend)).to.equal(true); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/unit/spec/services/builder.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Service: Builder', function() { 6 | // load the service's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | // instantiate service 10 | var Builder; 11 | beforeEach(inject(function(_Builder_) { 12 | Builder = _Builder_; 13 | })); 14 | 15 | it('should exists', function() { 16 | expect(Boolean(Builder)).to.equal(true); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/unit/spec/services/editor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Service: Editor', function() { 6 | // load the service's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | // instantiate service 10 | var Editor; 11 | beforeEach(inject(function(_Editor_) { 12 | Editor = _Editor_; 13 | })); 14 | 15 | it('should exists', function() { 16 | expect(Boolean(Editor)).to.equal(true); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/unit/spec/services/fileloader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Service: FileLoader', function() { 6 | // load the service's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | // instantiate service 10 | var FileLoader; 11 | beforeEach(inject(function(_FileLoader_) { 12 | FileLoader = _FileLoader_; 13 | })); 14 | 15 | it('should exists', function() { 16 | expect(Boolean(FileLoader)).to.equal(true); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/unit/spec/services/fold-state-manager.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Service: FoldStateManager', function() { 6 | // load the service's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | // instantiate service 10 | var FoldStateManager; 11 | beforeEach(inject(function(_FoldStateManager_) { 12 | if (!FoldStateManager) { 13 | FoldStateManager = _FoldStateManager_; 14 | } 15 | })); 16 | 17 | describe('#getFoldedTree', function() { 18 | it('keeps the $folded values', function() { 19 | var result = FoldStateManager.getFoldedTree( 20 | { 21 | info: {title: 'A'}, 22 | paths: { 23 | foo: { 24 | $folded: true 25 | } 26 | } 27 | }, 28 | { 29 | info: {title: 'B'}, 30 | paths: { 31 | foo: {} 32 | } 33 | } 34 | ); 35 | 36 | expect(result).to.deep.equal({ 37 | info: {title: 'B'}, 38 | paths: { 39 | foo: { 40 | $folded: true 41 | } 42 | } 43 | }); 44 | }); 45 | 46 | it('does not add extra nodes just because of $folded values', function() { 47 | var result = FoldStateManager.getFoldedTree( 48 | { 49 | info: {title: 'A'}, 50 | paths: { 51 | foo: { 52 | $folded: true 53 | } 54 | } 55 | }, 56 | { 57 | info: {title: 'B'}, 58 | paths: { 59 | bar: {} 60 | } 61 | } 62 | ); 63 | 64 | var expectation = { 65 | info: {title: 'B'}, 66 | paths: {} // bar should not be here as well as foo 67 | }; 68 | 69 | // why chai deep equal is not working? (to do) 70 | // expect(result).to.deep.equal(expectation); 71 | expect(JSON.stringify(result)).to.equal(JSON.stringify(expectation)); 72 | }); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/unit/spec/services/json-schema.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Service: JSONSchema', function() { 6 | var schema; 7 | 8 | // load the service's module 9 | beforeEach(angular.mock.module('SwaggerEditor')); 10 | 11 | // instantiate service 12 | var JSONSchema; 13 | beforeEach(inject(function(_JSONSchema_) { 14 | JSONSchema = _JSONSchema_; 15 | })); 16 | 17 | it('should exists', function() { 18 | expect(Boolean(JSONSchema)).to.equal(true); 19 | }); 20 | 21 | describe('normalizeJSONSchema', function() { 22 | beforeEach(function() { 23 | schema = { 24 | in: 'query', 25 | name: 'id' 26 | }; 27 | }); 28 | 29 | it('should have title when schema doesnt have title', function() { 30 | schema.type = 'string'; 31 | JSONSchema.normalizeJSONSchema(schema); 32 | expect(schema.title).to.be.equal('id'); 33 | }); 34 | 35 | it('should assign type to be string if it is file', function() { 36 | schema.type = 'file'; 37 | JSONSchema.normalizeJSONSchema(schema); 38 | expect(schema.type).to.be.equal('string'); 39 | expect(schema.format).to.be.equal('file'); 40 | }); 41 | 42 | it('should get the type from the items value if it has items', function() { 43 | schema.items = {type: 'string'}; 44 | JSONSchema.normalizeJSONSchema(schema); 45 | expect(schema.type).to.be.equal('array'); 46 | }); 47 | }); 48 | 49 | describe('resolveAllOf', function() { 50 | beforeEach(function() { 51 | schema = { 52 | properties: { 53 | foo: { 54 | allOf: [ 55 | { 56 | type: 'object', 57 | properties: { 58 | fistName: { 59 | type: 'string' 60 | } 61 | } 62 | }, 63 | { 64 | type: 'object', 65 | properties: { 66 | lastName: { 67 | type: 'string' 68 | } 69 | } 70 | } 71 | ] 72 | } 73 | } 74 | }; 75 | }); 76 | 77 | it('should resolve allOf deeply', function() { 78 | var resolved = { 79 | properties: { 80 | foo: { 81 | type: 'object', 82 | properties: { 83 | fistName: { 84 | type: 'string' 85 | }, 86 | lastName: { 87 | type: 'string' 88 | } 89 | } 90 | } 91 | } 92 | }; 93 | var newSchema = JSONSchema.resolveAllOf(schema); 94 | expect(newSchema).to.deep.equal(resolved); 95 | }); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /test/unit/spec/services/storage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('Service: Storage', function() { 6 | // load the service's module 7 | beforeEach(angular.mock.module('SwaggerEditor')); 8 | 9 | // instantiate service 10 | var Storage; 11 | beforeEach(inject(function(_Storage_) { 12 | Storage = _Storage_; 13 | })); 14 | 15 | it('should exists', function() { 16 | expect(Boolean(Storage)).to.equal(true); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /views/editor/editor.html: -------------------------------------------------------------------------------- 1 |
6 |
7 | -------------------------------------------------------------------------------- /views/main.html: -------------------------------------------------------------------------------- 1 |
2 |
8 | 9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |

Drop your file here

21 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /views/preview/preview.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | This preview is out of date. Please reload to see the latest. 4 |
5 | 6 | 10 | 11 | 12 | 15 | 16 | 17 | 20 | 21 | 22 | 25 | 26 | 27 | 30 | 31 | 32 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 51 | 52 |
53 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var webpack = require('webpack'); 5 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 6 | var NgAnnotatePlugin = require('ng-annotate-webpack-plugin'); 7 | var argv = require('minimist')(process.argv.slice(2)); 8 | var FONT_REGEX = /\.(ttf|eot|svg|woff|woff2|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/; 9 | 10 | var config = { 11 | devtool: 'source-map', 12 | 13 | entry: { 14 | app: ['./index.js'] 15 | }, 16 | 17 | output: { 18 | path: path.join(__dirname, 'dist'), 19 | filename: 'bundle.js', 20 | publicPath: 'dist/' 21 | }, 22 | 23 | resolve: { 24 | extensions: ['', '.js', '.json'], 25 | root: __dirname, 26 | modulesDirectories: ['node_modules'] 27 | }, 28 | 29 | plugins: [ 30 | new ExtractTextPlugin('styles.css') 31 | ], 32 | 33 | eslint: { 34 | configFile: './.eslintrc.js' 35 | }, 36 | 37 | module: { 38 | loaders: [ 39 | { 40 | test: /\.json$/, 41 | loader: 'json' 42 | }, 43 | { 44 | test: /\.worker.js$/, 45 | loader: 'worker' 46 | }, 47 | { 48 | test: /\.png$/, 49 | loader: "url", 50 | query: {mimetype: "image/png"} 51 | }, 52 | { 53 | test: /\.less$/, 54 | loader: ExtractTextPlugin.extract( 55 | 'css?sourceMap' + 56 | // minimize CSS in producion 57 | (argv.production ? '&minimize' : '') + 58 | '!less?sourceMap' 59 | ) 60 | }, 61 | { 62 | test: /images\/*\.svg$/, 63 | loader: 'svg-inline' 64 | }, 65 | { 66 | test: FONT_REGEX, 67 | loader: 'file?publicPath=/dist/' 68 | }, 69 | { 70 | test: /\.html$/, 71 | loader: 'html' 72 | } 73 | ], 74 | 75 | preLoaders: [ 76 | { 77 | test: /\.js$/, 78 | loader: 'eslint' 79 | } 80 | ] 81 | } 82 | }; 83 | 84 | // if --production is passed, ng-annotate and uglify the code 85 | if (argv.production) { 86 | console.info('This might take a while...'); 87 | 88 | config.plugins.unshift(new webpack.optimize.UglifyJsPlugin({mangle: true})); 89 | config.plugins.unshift(new NgAnnotatePlugin({add: true})); 90 | config.plugins.unshift(new webpack.NoErrorsPlugin()); 91 | } 92 | 93 | module.exports = config; 94 | --------------------------------------------------------------------------------