diff --git a/README.md b/README.md index f9b26265..41b83e7c 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,30 @@ # ReDoc +**OpenAPI/Swagger-generated API Reference Documentation** + [![Build Status](https://travis-ci.org/Rebilly/ReDoc.svg?branch=master)](https://travis-ci.org/Rebilly/ReDoc) [![Coverage Status](https://coveralls.io/repos/Rebilly/ReDoc/badge.svg?branch=master&service=github)](https://coveralls.io/github/Rebilly/ReDoc?branch=master) [![Tested on APIs.guru](http://api.apis.guru/badges/tested_on.svg)](https://APIs.guru) [![Code Climate](https://codeclimate.com/github/Rebilly/ReDoc/badges/gpa.svg)](https://codeclimate.com/github/Rebilly/ReDoc) [![David](https://david-dm.org/Rebilly/ReDoc/dev-status.svg)](https://david-dm.org/Rebilly/ReDoc#info=devDependencies) [![Stories in Ready](https://badge.waffle.io/Rebilly/ReDoc.png?label=ready&title=Ready)](https://waffle.io/Rebilly/ReDoc) [![npm](http://img.shields.io/npm/v/redoc.svg)](https://www.npmjs.com/package/redoc) [![Bower](http://img.shields.io/bower/v/redoc.svg)](http://bower.io/) [![License](https://img.shields.io/npm/l/redoc.svg)](https://github.com/Rebilly/ReDoc/blob/master/LICENSE) [![Browser Compatibility](https://saucelabs.com/browser-matrix/redoc.svg)](https://saucelabs.com/u/redoc) -Swagger-generated API Reference Documentation +![ReDoc demo](demo/redoc-demo.png) -[Live demo](http://rebilly.github.io/ReDoc/) +## [Live demo](http://rebilly.github.io/ReDoc/) + +## Roadmap + - [ ] docs pre-rendering (performance and SEO) + - [ ] ability to simple customization + - [ ] built-in API Console + +## Releases +We host latest and all the previous ReDoc releases on GitHub Pages-based **CDN**: +- `latest` release: https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js +- particular release, e.g. `v0.16.1`: https://rebilly.github.io/ReDoc/releases/v0.16.0/redoc.min.js ## Deployment -#### tl;dr +### TL;DR + ```html @@ -21,28 +34,24 @@ Swagger-generated API Reference Documentation - - - + + ``` +That's all folks! -#### 1. Install redoc +### 1. Install ReDoc (skip this step for CDN) Install using [bower](bower.io): bower install redoc @@ -51,12 +60,13 @@ or using [npm](https://docs.npmjs.com/getting-started/what-is-npm): npm install redoc --save -Alternatively, you can **reference redoc directly** from CDN: -- latest release: https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js -- particular release, e.g. v0.14.0: https://rebilly.github.io/ReDoc/releases/v0.14.0/redoc.min.js +### 2. Reference redoc script in HTML +For **CDN**: +```html + +``` -#### 2. Reference redoc script in HTML -Then reference [`redoc.min.js`](https://raw.githubusercontent.com/Rebilly/ReDoc/releases/dist/redoc.min.js) in your HTML page: +For bower: ```html ``` @@ -65,28 +75,29 @@ For npm: ``` -#### 3. Add `` element to your page +### 3. Add `` element to your page ```html - + ``` -#### 4. Enjoy :smile: +### 4. Enjoy :smile: ## Configuration -#### Swagger vendor extensions +### Swagger vendor extensions ReDoc makes use of the following [vendor extensions](http://swagger.io/specification/#vendorExtensions): * [`x-logo`](docs/redoc-vendor-extensions.md#x-logo) - is used to specify API logo * [`x-traitTag`](docs/redoc-vendor-extensions.md#x-traitTag) - useful for handling out common things like Pagination, Rate-Limits, etc * [`x-code-samples`](docs/redoc-vendor-extensions.md#x-code-samples) - specify operation code samples -#### Options -* `spec-url` - relative or absolute url to your spec file -* `scroll-y-offset` - If set, specifies a vertical scroll-offset. This is often useful when there are fixed positioned elements at the top of the page, such as navbars, headers etc. +### Options +* `spec-url` - relative or absolute url to your spec file; +* `scroll-y-offset` - If set, specifies a vertical scroll-offset. This is often useful when there are fixed positioned elements at the top of the page, such as navbars, headers etc; `scroll-y-offset` can be specified in various ways: - * **number**: A fixed number of pixels to be used as offset - * **selector**: selector of the element to be used for specifying the offset. The distance from the top of the page to the element's bottom will be used as offset. - * **function**: A getter function. Must return a number representing the offset (in pixels). + * **number**: A fixed number of pixels to be used as offset; + * **selector**: selector of the element to be used for specifying the offset. The distance from the top of the page to the element's bottom will be used as offset; + * **function**: A getter function. Must return a number representing the offset (in pixels); +* `suppress-warnings` - if set, warnings are not rendered at the top of page (they still are logged to the console). ## Advanced usage Instead of adding `spec-url` attribute to the `` element you can initialize ReDoc via globally exposed `Redoc` object: diff --git a/demo/index.html b/demo/index.html index 27b4769f..28fb0b47 100644 --- a/demo/index.html +++ b/demo/index.html @@ -4,19 +4,20 @@ ReDoc - - + + diff --git a/demo/main.css b/demo/main.css index e927f4e6..c0150c92 100644 --- a/demo/main.css +++ b/demo/main.css @@ -1,6 +1,3 @@ -/*@import url(http://fonts.googleapis.com/css?family=Roboto:300,400,700); -@import url(http://fonts.googleapis.com/css?family=Montserrat:300,400);*/ - body { margin: 0; padding-top: 50px; @@ -26,6 +23,7 @@ body { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; text-size-adjust: 100%; + font-family: Monserrat, sans-serif; } nav input, nav button { @@ -49,11 +47,11 @@ nav input { width: 50%; box-sizing: border-box; max-width: 500px; + padding: 0 10px; color: #555; background-color: #fff; border: 1px solid #ccc; - border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); box-shadow: inset 0 1px 1px rgba(0,0,0,.075); -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; @@ -80,7 +78,6 @@ nav button { -ms-user-select: none; user-select: none; border: 1px solid #ccc; - border-radius: 4px; } nav button:hover { diff --git a/demo/main.js b/demo/main.js index 2762004b..95bf33b2 100644 --- a/demo/main.js +++ b/demo/main.js @@ -2,19 +2,12 @@ 'use strict'; var schemaUrlForm = document.getElementById('schema-url-form'); - var schemaUrlInput = document.getElementById('schema-url-input'); - schemaUrlForm.addEventListener('submit', function(event) { - event.preventDefault(); - event.stopPropagation(); - location.search = updateQueryStringParameter(location.search, 'url', schemaUrlInput.value) - return false; - }) + var schemaUrlInput; var url = window.location.search.match(/url=([^&]+)/); if (url && url.length > 1) { url = decodeURIComponent(url[1]); document.getElementsByTagName('redoc')[0].setAttribute('spec-url', url); - schemaUrlInput.value = url; } function updateQueryStringParameter(uri, key, value) { @@ -31,5 +24,35 @@ return uri + separator + key + "=" + value + hash; } } + + var specs = document.querySelector('#specs'); + specs.addEventListener('dom-change', function() { + schemaUrlForm = document.getElementById('schema-url-form'); + schemaUrlInput = document.getElementById('schema-url-input'); + + schemaUrlForm.addEventListener('submit', function(event) { + console.log('test') + event.preventDefault(); + event.stopPropagation(); + location.search = updateQueryStringParameter(location.search, 'url', schemaUrlInput.value) + return false; + }) + + schemaUrlInput.addEventListener('mousedown', function(e) { + e.stopPropagation(); + }); + schemaUrlInput.value = url; + specs.specs = [ + 'https://api.apis.guru/v2/specs/instagram.com/1.0.0/swagger.yaml', + 'https://api.apis.guru/v2/specs/googleapis.com/calendar/v3/swagger.yaml', + 'https://api.apis.guru/v2/specs/data2crm.com/1/swagger.yaml', + 'https://api.apis.guru/v2/specs/graphhopper.com/1.0/swagger.yaml' + ]; + var $specInput = document.getElementById('spec-input'); + $specInput.addEventListener('value-changed', function(e) { + schemaUrlInput.value = e.detail.value; + location.search = updateQueryStringParameter(location.search, 'url', schemaUrlInput.value); + }); + }); //window.redocDebugMode = true; })(); diff --git a/demo/redoc-demo.png b/demo/redoc-demo.png new file mode 100644 index 00000000..550a1575 Binary files /dev/null and b/demo/redoc-demo.png differ diff --git a/demo/swagger.yaml b/demo/swagger.yaml index 5034a924..47cad699 100644 --- a/demo/swagger.yaml +++ b/demo/swagger.yaml @@ -1,10 +1,32 @@ swagger: '2.0' schemes: - http + - https host: petstore.swagger.io basePath: /v2 info: - description: 'This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.' + description: | + This is a sample server Petstore server. + You can find out more about Swagger at + [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). + For this sample, you can use the api key `special-key` to test the authorization filters. + # Introduction + This API is documented in **OpenAPI format** and is based on + [Pestore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team. + It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo) + tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard + OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md). + # OpenAPI Specification + This API is documented in **OpenAPI format** and is based on + [Pestore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team. + It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo) + tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard + OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md). + # Cross-Origin Resource Sharing + This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/). + And that allows cross-domain communication from the browser. + All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site. + version: 1.0.0 title: Swagger Petstore termsOfService: 'http://swagger.io/terms/' @@ -16,47 +38,15 @@ info: name: Apache 2.0 url: 'http://www.apache.org/licenses/LICENSE-2.0.html' externalDocs: - description: Find out more about Swagger - url: 'http://swagger.io' + description: Find out how to create Github repo for your OpenAPI spec. + url: 'https://github.com/Rebilly/generator-openapi-repo' tags: - - name: Pagination - x-traitTag: true - description: |- - Sometimes you just can't get enough. For this reason, we've provided a convenient way to access more data in any request for sequential data. Simply call the url in the next_url parameter and we'll respond with the next set of data. - ```json - { - ... - "pagination": { - "next_url": - "https://api.instagram.com/v1/tags/puppy/media/recent?access_token=fb2e77d.47a0479900504cb3ab4a1f626d174d2d&max_id=13872296", - "next_max_id": "13872296" - } - } - ``` - - On views where pagination is present, we also support the `count` - parameter. Simply set this to the number of items you'd like to receive. Note that the default values should be fine for most applications - but if you decide to increase this number there is a maximum value defined on each endpoint. - externalDocs: - description: Find out more - url: 'http://swagger.io' - - name: JSONP - x-traitTag: true - description: "If you're writing an AJAX application, and you'd like to wrap our response with a callback, all you have to do is specify a callback parameter with any API call:\n```\n https://api.instagram.com/v1/tags/coffee/media/recent?access_token=fb2e77d.47a0479900504cb3ab4a1f626d174d2d&callback=callbackFunction\n```\nWould respond with:\n```js\ncallbackFunction({\n ...\n});\n``` \n > Example of markdown blockquote" - externalDocs: - description: Find out more - url: 'http://swagger.io' - name: pet description: Everything about your Pets - externalDocs: - description: Find out more - url: 'http://swagger.io' - name: store description: Access to Petstore orders - name: user description: Operations about user - externalDocs: - description: Find out more about our store - url: 'http://swagger.io' securityDefinitions: petstore_auth: type: oauth2 @@ -75,7 +65,7 @@ paths: tags: - pet summary: Add a new pet to the store - description: '' + description: Add new pet to the store inventory. operationId: addPet consumes: - application/json @@ -154,7 +144,6 @@ paths: get: tags: - pet - - JSONP summary: Find pet by ID description: Returns a single pet operationId: getPetById @@ -242,40 +231,6 @@ paths: - 'write:pets' - 'read:pets' '/pet/{petId}/uploadImage': - get: - tags: - - pet - summary: uploads an image - description: '' - operationId: uploadFile - consumes: - - image/jpeg - - image/png - produces: - - image/jpeg - - image/png - parameters: - - name: petId - in: path - description: ID of pet to update - required: true - type: integer - format: int64 - - name: file - in: body - description: file to upload - required: false - schema: - type: file - responses: - '200': - description: successful operation - schema: - type: file - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' post: tags: - pet @@ -316,8 +271,6 @@ paths: get: tags: - pet - - Pagination - - JSONP summary: Finds Pets by status description: Multiple status values can be provided with comma seperated strings operationId: findPetsByStatus @@ -355,8 +308,6 @@ paths: get: tags: - pet - - Pagination - - JSONP summary: Finds Pets by tags description: 'Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.' operationId: findPetsByTags @@ -389,7 +340,6 @@ paths: get: tags: - store - - JSONP summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory @@ -434,7 +384,6 @@ paths: get: tags: - store - - JSONP summary: Find purchase order by ID description: 'For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions' operationId: getOrderById @@ -504,7 +453,6 @@ paths: get: tags: - user - - JSONP summary: Get user by user name description: '' operationId: getUserByName @@ -642,6 +590,8 @@ paths: description: successful operation schema: type: string + examples: + application/json: OK headers: X-Rate-Limit: type: integer @@ -699,10 +649,13 @@ definitions: type: object properties: id: - type: integer - format: int64 + description: Category ID + allOf: + - $ref: '#/definitions/Id' name: + description: Category name type: string + minLength: 1 xml: name: Category Dog: @@ -714,24 +667,44 @@ definitions: packSize: type: integer format: int32 - description: the size of the pack the dog is from - default: 0 - minimum: 0 + description: The size of the pack the dog is from + default: 1 + minimum: 1 required: - packSize + HoneyBee: + description: A representation of a honey bee + allOf: + - $ref: '#/definitions/Pet' + - type: object + properties: + honeyPerDay: + type: number + description: Average amount of honey produced per day in ounces + example: 3.14 + required: + - honeyPerDay + Id: + type: integer + format: int64 Order: type: object properties: id: - type: integer - format: int64 + description: Order ID + allOf: + - $ref: '#/definitions/Id' petId: - type: integer - format: int64 + description: Pet ID + allOf: + - $ref: '#/definitions/Id' quantity: type: integer format: int32 + minimum: 1 + default: 1 shipDate: + description: Estimated ship date type: string format: date-time status: @@ -742,6 +715,7 @@ definitions: - approved - delivered complete: + description: Indicates whenever order was completed or not type: boolean default: false xml: @@ -754,23 +728,31 @@ definitions: discriminator: petType properties: petType: + description: Type of a pet type: string id: - type: integer - format: int64 + description: Pet ID + allOf: + - $ref: '#/definitions/Id' category: - $ref: '#/definitions/Category' + description: Categories this pet belongs to + allOf: + - $ref: '#/definitions/Category' name: + description: The name given to a pet type: string - example: doggie + example: Guru photoUrls: + description: The list of URL to a cute photos featuring pet type: array xml: name: photoUrl wrapped: true items: type: string + format: url tags: + description: Tags attached to the pet type: array xml: name: tag @@ -779,7 +761,7 @@ definitions: $ref: '#/definitions/Tag' status: type: string - description: pet status in the store + description: Pet status in the store enum: - available - pending @@ -790,33 +772,56 @@ definitions: type: object properties: id: - type: integer - format: int64 + description: Tag ID + allOf: + - $ref: '#/definitions/Id' name: + description: Tag name type: string + minLength: 1 xml: name: Tag User: type: object properties: id: - type: integer - format: int64 + description: User ID + $ref: '#/definitions/Id' username: + description: User supplied username type: string + minLength: 4 + example: John78 firstName: + description: User first name type: string + minLength: 1 + example: John lastName: + description: User last name type: string + minLength: 1 + example: Smith email: + description: User email address type: string + format: email + example: john.smith@example.com password: type: string + description: 'User password, MUST contain a mix of upper and lower case letters, as well as digits' + format: password + minLength: 8 + pattern: '(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])' + example: drowssaP123 phone: + description: User phone number in international format type: string + pattern: "^\\+(?:[0-9]-?){6,14}[0-9]$" + example: +1-202-555-0192 userStatus: + description: User status type: integer format: int32 - description: User Status xml: name: User diff --git a/docs/redoc-vendor-extensions.md b/docs/redoc-vendor-extensions.md index ab5d54ec..9f9ea251 100644 --- a/docs/redoc-vendor-extensions.md +++ b/docs/redoc-vendor-extensions.md @@ -1,9 +1,9 @@ # ReDoc vendor extensions ReDoc makes use of the following [vendor extensions](http://swagger.io/specification/#vendorExtensions) -### [Info Object](http://swagger.io/specification/#infoObject) vendor extensions - -#### x-logo +### Info Object vendor extensions +Extends OpenAPI [Info Object](http://swagger.io/specification/#infoObject) +#### x-logo | Field Name | Type | Description | | :------------- | :-----------: | :---------- | @@ -47,9 +47,9 @@ info: -### [Tag object](http://swagger.io/specification/#tagObject) vendor extensions - -#### x-traitTag +### Tag Object vendor extensions +Extends OpenAPI [Tag Object](http://swagger.io/specification/#tagObject) +#### x-traitTag | Field Name | Type | Description | | :------------- | :------: | :---------- | | x-traitTag | boolean | In Swagger two operations can have multiply tags. This property distinguish between tags that are used to group operations (default) from tags that are used to mark operation with certain trait (`true` value) | @@ -74,9 +74,9 @@ description: Pagination description (can use markdown syntax) x-traitTag: true ``` -### [Operation Object](http://swagger.io/specification/#operationObject) vendor extensions - -#### x-code-samples +### Operation Object vendor extensions +Extends OpenAPI [Operation Object](http://swagger.io/specification/#operationObject) +#### x-code-samples | Field Name | Type | Description | | :------------- | :------: | :---------- | | x-code-samples | [ [Code Sample Object](#codeSampleObject) ] | A list of code samples associated with operation | diff --git a/lib/components/ApiInfo/api-info.html b/lib/components/ApiInfo/api-info.html index 1c8151f5..5b356438 100644 --- a/lib/components/ApiInfo/api-info.html +++ b/lib/components/ApiInfo/api-info.html @@ -1,21 +1,21 @@
-

{{data.title}} ({{data.version}})

-

-

- - Contact: - - {{data.contact.name || data.contact.url}} - - {{data.contact.email}} - - License: - {{data.license.name}} - {{data.license.name}} - -

+

{{info.title}} ({{info.version}})

Download OpenAPI (fka Swagger) specification: Download

+

+

+ + Contact: + + {{info.contact.name || info.contact.url}} + + {{info.contact.email}} + + License: + {{info.license.name}} + {{info.license.name}} + +

diff --git a/lib/components/ApiInfo/api-info.scss b/lib/components/ApiInfo/api-info.scss index 762471b9..5ec113ef 100644 --- a/lib/components/ApiInfo/api-info.scss +++ b/lib/components/ApiInfo/api-info.scss @@ -6,6 +6,8 @@ :host > div { width: 60%; + padding: 40px; + box-sizing: border-box; } a.openapi-button { @@ -15,3 +17,8 @@ a.openapi-button { margin-left: 0.5em; font-weight: normal; } + +:host [section] { + padding-top: 60px; + margin-top: 20px; +} diff --git a/lib/components/ApiInfo/api-info.spec.ts b/lib/components/ApiInfo/api-info.spec.ts index acaebf1c..d8b6e2ee 100644 --- a/lib/components/ApiInfo/api-info.spec.ts +++ b/lib/components/ApiInfo/api-info.spec.ts @@ -2,6 +2,7 @@ import { getChildDebugElement } from '../../../tests/helpers'; import { Component } from '@angular/core'; +import { OptionsService } from '../../services/index'; import { inject, @@ -17,8 +18,14 @@ describe('Redoc components', () => { let builder; let component; let fixture; + let opts; - beforeEach(async(inject([TestComponentBuilder, SpecManager], (tcb, specMgr) => { + beforeEach(async(inject([TestComponentBuilder, SpecManager, OptionsService], (tcb, specMgr, _opts) => { + opts = _opts; + opts.options = { + scrollYOffset: () => 0, + $scrollParent: window + }; builder = tcb; return specMgr.load('/tests/schemas/api-info-test.json'); }))); @@ -32,8 +39,8 @@ describe('Redoc components', () => { it('should init component data', () => { expect(component).not.toBeNull(); - expect(component.data).not.toBeNull(); - component.data.title.should.be.equal('Swagger Petstore'); + expect(component.info).not.toBeNull(); + component.info.title.should.be.equal('Swagger Petstore'); }); it('should render api name and version', () => { diff --git a/lib/components/ApiInfo/api-info.ts b/lib/components/ApiInfo/api-info.ts index db5db8a0..395e82ed 100644 --- a/lib/components/ApiInfo/api-info.ts +++ b/lib/components/ApiInfo/api-info.ts @@ -1,7 +1,7 @@ 'use strict'; import { SpecManager, RedocComponent, BaseComponent } from '../base'; -import { OptionsService } from '../../services/index'; +import { OptionsService, MenuService } from '../../services/index'; @RedocComponent({ selector: 'api-info', @@ -9,17 +9,17 @@ import { OptionsService } from '../../services/index'; templateUrl: './api-info.html' }) export class ApiInfo extends BaseComponent { - data: any; + info: any; specUrl: String; - constructor(specMgr:SpecManager, private optionsService:OptionsService) { + constructor(specMgr:SpecManager, private optionsService:OptionsService, private menuServ: MenuService) { super(specMgr); } - prepareModel() { - this.data = this.componentSchema.info; + init() { + this.info = this.componentSchema.info; this.specUrl = this.optionsService.options.specUrl; - if (parseInt(this.data.version.substring(0, 1)) !== NaN) { - this.data.version = 'v' + this.data.version; + if (parseInt(this.info.version.substring(0, 1)) !== NaN) { + this.info.version = 'v' + this.info.version; } } } diff --git a/lib/components/ApiLogo/api-logo.html b/lib/components/ApiLogo/api-logo.html index 3cf09f27..897e3583 100644 --- a/lib/components/ApiLogo/api-logo.html +++ b/lib/components/ApiLogo/api-logo.html @@ -1 +1 @@ - + diff --git a/lib/components/ApiLogo/api-logo.spec.ts b/lib/components/ApiLogo/api-logo.spec.ts index 79484ff0..cc541c4e 100644 --- a/lib/components/ApiLogo/api-logo.spec.ts +++ b/lib/components/ApiLogo/api-logo.spec.ts @@ -35,11 +35,11 @@ describe('Redoc components', () => { it('should init component data', () => { expect(component).not.toBeNull(); - expect(component.data).not.toBeNull(); + expect(component.logo).not.toBeNull(); }); it('should not display image when no x-logo', () => { - component.data.should.be.empty(); + component.logo.should.be.empty(); let nativeElement = getChildDebugElement(fixture.debugElement, 'api-logo').nativeElement; let imgElement = nativeElement.querySelector('img'); expect(imgElement).toBeNull(); @@ -49,8 +49,8 @@ describe('Redoc components', () => { }); it('should load values from spec and use transparent bgColor by default', () => { - component.data.imgUrl.should.endWith('petstore-logo.png'); - component.data.bgColor.should.be.equal('transparent'); + component.logo.imgUrl.should.endWith('petstore-logo.png'); + component.logo.bgColor.should.be.equal('transparent'); }); }); }); diff --git a/lib/components/ApiLogo/api-logo.ts b/lib/components/ApiLogo/api-logo.ts index fb1edad3..4127fbe6 100644 --- a/lib/components/ApiLogo/api-logo.ts +++ b/lib/components/ApiLogo/api-logo.ts @@ -8,16 +8,16 @@ import {RedocComponent, BaseComponent, SpecManager} from '../base'; templateUrl: './api-logo.html' }) export class ApiLogo extends BaseComponent { - data:any = {}; + logo:any = {}; constructor(specMgr:SpecManager) { super(specMgr); } - prepareModel() { + init() { let logoInfo = this.componentSchema.info['x-logo']; if (!logoInfo) return; - this.data.imgUrl = logoInfo.url; - this.data.bgColor = logoInfo.backgroundColor || 'transparent'; + this.logo.imgUrl = logoInfo.url; + this.logo.bgColor = logoInfo.backgroundColor || 'transparent'; } } diff --git a/lib/components/JsonSchema/json-schema-lazy.ts b/lib/components/JsonSchema/json-schema-lazy.ts index 598e489d..0dbc137a 100644 --- a/lib/components/JsonSchema/json-schema-lazy.ts +++ b/lib/components/JsonSchema/json-schema-lazy.ts @@ -23,9 +23,11 @@ export class JsonSchemaLazy implements OnDestroy, AfterViewInit { @Input() nestOdd: boolean; @Input() childFor: string; @Input() isArray: boolean; + disableLazy: boolean = false; loaded: boolean = false; constructor(private specMgr:SpecManager, private location:ViewContainerRef, private elementRef:ElementRef, private resolver:ComponentResolver, private optionsService:OptionsService, private _renderer: Renderer) { + this.disableLazy = this.optionsService.options.disableLazySchemas; } normalizePointer() { @@ -67,7 +69,7 @@ export class JsonSchemaLazy implements OnDestroy, AfterViewInit { // skip caching view with tabs inside (discriminator) // as it needs attached controller - if (compRef.instance.hasDescendants || compRef.instance._hasSubSchemas) { + if (!this.disableLazy && (compRef.instance.hasDescendants || compRef.instance._hasSubSchemas)) { this._loadAfterSelf(); return; } @@ -85,11 +87,7 @@ export class JsonSchemaLazy implements OnDestroy, AfterViewInit { } ngAfterViewInit() { - if (this.optionsService.options.disableLazySchemas) { - this._loadAfterSelf(); - return; - } - if (!this.auto) return; + if (!this.auto && !this.disableLazy) return; this.loadCached(); } diff --git a/lib/components/JsonSchema/json-schema.spec.ts b/lib/components/JsonSchema/json-schema.spec.ts index 962116f2..1d633b10 100644 --- a/lib/components/JsonSchema/json-schema.spec.ts +++ b/lib/components/JsonSchema/json-schema.spec.ts @@ -23,6 +23,7 @@ describe('Redoc components', () => { builder = tcb; specMgr = _spec; })); + beforeEach(() => { fixture = builder.createSync(TestAppComponent); let debugEl = getChildDebugElement(fixture.debugElement, 'json-schema'); @@ -37,14 +38,14 @@ describe('Redoc components', () => { }); it('should set isTrivial for non-object/array types', () => { - component.pointer = ''; + component.pointer = '#'; (specMgr)._schema = {type: 'string'}; fixture.detectChanges(); component.schema.isTrivial.should.be.true(); }); it('should use < * > notation for prop without type', () => { - component.pointer = ''; + component.pointer = '#'; (specMgr)._schema = {type: 'object', properties: { test: {} }}; diff --git a/lib/components/JsonSchema/json-schema.ts b/lib/components/JsonSchema/json-schema.ts index b69f4d38..83649f80 100644 --- a/lib/components/JsonSchema/json-schema.ts +++ b/lib/components/JsonSchema/json-schema.ts @@ -16,7 +16,7 @@ import { Zippy } from '../../shared/components/Zippy/zippy'; detect: true }) export class JsonSchema extends BaseComponent { - schema: any; + schema: any = {}; activeDescendant:any = {}; hasDescendants: boolean = false; _hasSubSchemas: boolean = false; @@ -68,7 +68,8 @@ export class JsonSchema extends BaseComponent { this.selectDescendant(0); } - prepareModel() { + init() { + if (!this.pointer) return; if (this.nestOdd) { this._renderer.setElementAttribute(this._elementRef.nativeElement, 'nestodd', 'true'); } diff --git a/lib/components/Method/method.html b/lib/components/Method/method.html index 6821882b..d4347097 100644 --- a/lib/components/Method/method.html +++ b/lib/components/Method/method.html @@ -1,13 +1,13 @@

- {{data.summary}} + {{method.summary}}

-
- {{tag}} + -

+

@@ -16,15 +16,15 @@
Definition
-
{{data.httpMethod}}
+
{{method.httpMethod}}
{{data.apiUrl}}{{data.path}}{{method.apiUrl}}{{method.path}}
-
+

- +
diff --git a/lib/components/Method/method.scss b/lib/components/Method/method.scss index aa373c10..79b77d57 100644 --- a/lib/components/Method/method.scss +++ b/lib/components/Method/method.scss @@ -1,5 +1,5 @@ @import '../../shared/styles/variables'; -@import '../../shared/styles/share-link'; + :host { padding-bottom: 100px; @@ -11,6 +11,10 @@ border-bottom: 0; } +h2 { + color: $secondary-color; +} + responses-list, params-list { display: block; } diff --git a/lib/components/Method/method.spec.ts b/lib/components/Method/method.spec.ts index 21cdc51a..07f187ce 100644 --- a/lib/components/Method/method.spec.ts +++ b/lib/components/Method/method.spec.ts @@ -34,14 +34,14 @@ describe('Redoc components', () => { }); it('should init basic component data', () => { - component.data.apiUrl.should.be.equal('http://petstore.swagger.io/v2'); - component.data.httpMethod.should.be.equal('put'); - component.data.path.should.be.equal('/user/{username}'); + component.method.apiUrl.should.be.equal('http://petstore.swagger.io/v2'); + component.method.httpMethod.should.be.equal('put'); + component.method.path.should.be.equal('/user/{username}'); }); it('should main tag', () => { - component.data.methodInfo.tags.should.be.empty(); + component.method.info.tags.should.be.empty(); }); }); }); diff --git a/lib/components/Method/method.ts b/lib/components/Method/method.ts index 3df30528..caa791b2 100644 --- a/lib/components/Method/method.ts +++ b/lib/components/Method/method.ts @@ -20,25 +20,25 @@ import { SchemaHelper } from '../../services/schema-helper.service'; detect: true }) export class Method extends BaseComponent { - data:any; + method:any; @Input() tag:string; constructor(specMgr:SpecManager) { super(specMgr); } - prepareModel() { - this.data = {}; - this.data.apiUrl = this.specMgr.apiUrl; - this.data.httpMethod = JsonPointer.baseName(this.pointer); - this.data.path = JsonPointer.baseName(this.pointer, 2); - this.data.methodInfo = this.componentSchema; - this.data.methodInfo.tags = this.filterMainTags(this.data.methodInfo.tags); - this.data.bodyParam = this.findBodyParam(); - this.data.summary = SchemaHelper.methodSummary(this.componentSchema); + init() { + this.method = {}; + this.method.apiUrl = this.specMgr.apiUrl; + this.method.httpMethod = JsonPointer.baseName(this.pointer); + this.method.path = JsonPointer.baseName(this.pointer, 2); + this.method.info = this.componentSchema; + this.method.info.tags = this.filterMainTags(this.method.info.tags); + this.method.bodyParam = this.findBodyParam(); + this.method.summary = SchemaHelper.methodSummary(this.componentSchema); if (this.componentSchema.operationId) { - this.data.methodAnchor = 'operation/' + encodeURIComponent(this.componentSchema.operationId); + this.method.anchor = 'operation/' + encodeURIComponent(this.componentSchema.operationId); } else { - this.data.methodAnchor = 'tag/' + encodeURIComponent(this.tag + this.pointer); + this.method.anchor = this.tag + encodeURIComponent(this.pointer); } } diff --git a/lib/components/MethodsList/methods-list.html b/lib/components/MethodsList/methods-list.html index 6f42f8b6..5b632007 100644 --- a/lib/components/MethodsList/methods-list.html +++ b/lib/components/MethodsList/methods-list.html @@ -1,10 +1,10 @@
-
-
+
+

{{tag.name}}

+ [attr.section]="method.tag" [tag]="method.tag" [attr.operation-id]="method.operationId">
diff --git a/lib/components/MethodsList/methods-list.scss b/lib/components/MethodsList/methods-list.scss index ff1465c3..deb9b93b 100644 --- a/lib/components/MethodsList/methods-list.scss +++ b/lib/components/MethodsList/methods-list.scss @@ -1,5 +1,4 @@ @import '../../shared/styles/variables'; -@import '../../shared/styles/share-link'; .tag-info { padding: 40px; @@ -21,6 +20,7 @@ color: $headers-color; text-transform: capitalize; font-weight: normal; + margin-top: 0; } .methods { diff --git a/lib/components/MethodsList/methods-list.spec.ts b/lib/components/MethodsList/methods-list.spec.ts index b8e6be02..ad0728be 100644 --- a/lib/components/MethodsList/methods-list.spec.ts +++ b/lib/components/MethodsList/methods-list.spec.ts @@ -35,12 +35,12 @@ describe('Redoc components', () => { }); it('should get correct tags list', () => { - expect(component.data.tags).not.toBeNull(); - component.data.tags.should.have.lengthOf(2); - component.data.tags[0].name.should.be.equal('traitTag'); - component.data.tags[0].methods.should.be.empty(); - component.data.tags[1].name.should.be.equal('tag1'); - component.data.tags[1].methods.should.have.lengthOf(2); + expect(component.tags).not.toBeNull(); + component.tags.should.have.lengthOf(2); + component.tags[0].name.should.be.equal('traitTag'); + component.tags[0].methods.should.be.empty(); + component.tags[1].name.should.be.equal('tag1'); + component.tags[1].methods.should.have.lengthOf(2); }); }); }); diff --git a/lib/components/MethodsList/methods-list.ts b/lib/components/MethodsList/methods-list.ts index d3dd6527..1fabe686 100644 --- a/lib/components/MethodsList/methods-list.ts +++ b/lib/components/MethodsList/methods-list.ts @@ -15,26 +15,21 @@ import { SchemaHelper } from '../../services/index'; detect: true }) export class MethodsList extends BaseComponent { - data:any; + tags:Array = []; constructor(specMgr:SpecManager) { super(specMgr); } - prepareModel() { - this.data = {}; - // follow SwaggerUI behavior for cases when one method has more than one tag: - // duplicate methods - + init() { let tags = SchemaHelper.buildMenuTree(this.specMgr.schema); - tags.forEach(tagInfo => { + this.tags = tags.filter(tagInfo => !tagInfo.virtual); + this.tags.forEach(tagInfo => { // inject tag name into method info tagInfo.methods = tagInfo.methods || []; tagInfo.methods.forEach(method => { - method.tag = tagInfo.name; + method.tag = tagInfo.id; }); }); - this.data.tags = tags; - // TODO: check $ref field } trackByPointer(idx, el) { diff --git a/lib/components/ParamsList/params-list.html b/lib/components/ParamsList/params-list.html index dbba3356..6601e4dc 100644 --- a/lib/components/ParamsList/params-list.html +++ b/lib/components/ParamsList/params-list.html @@ -1,5 +1,5 @@ -
Parameters
-