mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-25 01:53:44 +03:00
feat: add callbacks support (#1224)
Co-authored-by: Jonathan Bailey <jonathan_bailey@bose.com> Co-authored-by: Roman Hotsiy <gotsijroman@gmail.com>
This commit is contained in:
parent
5bd2e6227b
commit
57e93ec435
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
|
@ -50,11 +50,13 @@ $ npm run unit
|
||||||
|
|
||||||
# run e2e tests
|
# run e2e tests
|
||||||
$ npm run e2e
|
$ npm run e2e
|
||||||
|
# Make sure you have created bundle before running e2e test
|
||||||
|
# E.g. run `npm run bundle` and wait for the finishing process.
|
||||||
|
|
||||||
# open cypress UI to debug e2e test
|
# open cypress UI to debug e2e test
|
||||||
$ npm run cy:open
|
$ npm run cy:open
|
||||||
|
|
||||||
# run the full test suite, include linting / unit / e2e
|
# run the unit tests (includes linting and license checks)
|
||||||
$ npm test
|
$ npm test
|
||||||
|
|
||||||
# prepare bundles
|
# prepare bundles
|
||||||
|
|
|
@ -489,6 +489,234 @@ paths:
|
||||||
description: Invalid ID supplied
|
description: Invalid ID supplied
|
||||||
'404':
|
'404':
|
||||||
description: Order not found
|
description: Order not found
|
||||||
|
/store/subscribe:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- store
|
||||||
|
summary: Subscribe to the Store events
|
||||||
|
description: Add subscription for a store events
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
callbackUrl:
|
||||||
|
type: string
|
||||||
|
format: uri
|
||||||
|
description: This URL will be called by the server when the desired event will occur
|
||||||
|
example: https://myserver.com/send/callback/here
|
||||||
|
eventName:
|
||||||
|
type: string
|
||||||
|
description: Event name for the subscription
|
||||||
|
enum:
|
||||||
|
- orderInProgress
|
||||||
|
- orderShipped
|
||||||
|
- orderDelivered
|
||||||
|
example: orderInProgress
|
||||||
|
required:
|
||||||
|
- callbackUrl
|
||||||
|
- eventName
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: Subscription added
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
subscriptionId:
|
||||||
|
type: string
|
||||||
|
example: AAA-123-BBB-456
|
||||||
|
callbacks:
|
||||||
|
orderInProgress:
|
||||||
|
'{$request.body#/callbackUrl}?event={$request.body#/eventName}':
|
||||||
|
servers:
|
||||||
|
- url: //callback-url.path-level/v1
|
||||||
|
description: Path level server 1
|
||||||
|
- url: //callback-url.path-level/v2
|
||||||
|
description: Path level server 2
|
||||||
|
post:
|
||||||
|
summary: Order in Progress (Summary)
|
||||||
|
description: A callback triggered every time an Order is updated status to "inProgress" (Description)
|
||||||
|
externalDocs:
|
||||||
|
description: Find out more
|
||||||
|
url: 'https://more-details.com/demo'
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
orderId:
|
||||||
|
type: string
|
||||||
|
example: '123'
|
||||||
|
timestamp:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
example: '2018-10-19T16:46:45Z'
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
example: 'inProgress'
|
||||||
|
application/xml:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
orderId:
|
||||||
|
type: string
|
||||||
|
example: '123'
|
||||||
|
example: |
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<orderId>123</orderId>
|
||||||
|
<status>inProgress</status>
|
||||||
|
<timestamp>2018-10-19T16:46:45Z</timestamp>
|
||||||
|
</root>
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Callback successfully processed and no retries will be performed
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
someProp:
|
||||||
|
type: string
|
||||||
|
example: '123'
|
||||||
|
'299':
|
||||||
|
description: Response for cancelling subscription
|
||||||
|
'500':
|
||||||
|
description: Callback processing failed and retries will be performed
|
||||||
|
x-code-samples:
|
||||||
|
- lang: 'C#'
|
||||||
|
source: |
|
||||||
|
PetStore.v1.Pet pet = new PetStore.v1.Pet();
|
||||||
|
pet.setApiKey("your api key");
|
||||||
|
pet.petType = PetStore.v1.Pet.TYPE_DOG;
|
||||||
|
pet.name = "Rex";
|
||||||
|
// set other fields
|
||||||
|
PetStoreResponse response = pet.create();
|
||||||
|
if (response.statusCode == HttpStatusCode.Created)
|
||||||
|
{
|
||||||
|
// Successfully created
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Something wrong -- check response for errors
|
||||||
|
Console.WriteLine(response.getRawResponse());
|
||||||
|
}
|
||||||
|
- lang: PHP
|
||||||
|
source: |
|
||||||
|
$form = new \PetStore\Entities\Pet();
|
||||||
|
$form->setPetType("Dog");
|
||||||
|
$form->setName("Rex");
|
||||||
|
// set other fields
|
||||||
|
try {
|
||||||
|
$pet = $client->pets()->create($form);
|
||||||
|
} catch (UnprocessableEntityException $e) {
|
||||||
|
var_dump($e->getErrors());
|
||||||
|
}
|
||||||
|
put:
|
||||||
|
description: Order in Progress (Only Description)
|
||||||
|
servers:
|
||||||
|
- url: //callback-url.operation-level/v1
|
||||||
|
description: Operation level server 1 (Operation override)
|
||||||
|
- url: //callback-url.operation-level/v2
|
||||||
|
description: Operation level server 2 (Operation override)
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
orderId:
|
||||||
|
type: string
|
||||||
|
example: '123'
|
||||||
|
timestamp:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
example: '2018-10-19T16:46:45Z'
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
example: 'inProgress'
|
||||||
|
application/xml:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
orderId:
|
||||||
|
type: string
|
||||||
|
example: '123'
|
||||||
|
example: |
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<orderId>123</orderId>
|
||||||
|
<status>inProgress</status>
|
||||||
|
<timestamp>2018-10-19T16:46:45Z</timestamp>
|
||||||
|
</root>
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Callback successfully processed and no retries will be performed
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
someProp:
|
||||||
|
type: string
|
||||||
|
example: '123'
|
||||||
|
orderShipped:
|
||||||
|
'{$request.body#/callbackUrl}?event={$request.body#/eventName}':
|
||||||
|
post:
|
||||||
|
description: |
|
||||||
|
Very long description
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
|
||||||
|
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
|
||||||
|
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||||
|
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
||||||
|
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
|
||||||
|
culpa qui officia deserunt mollit anim id est laborum.
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
orderId:
|
||||||
|
type: string
|
||||||
|
example: '123'
|
||||||
|
timestamp:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
example: '2018-10-19T16:46:45Z'
|
||||||
|
estimatedDeliveryDate:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
example: '2018-11-11T16:00:00Z'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Callback successfully processed and no retries will be performed
|
||||||
|
orderDelivered:
|
||||||
|
'http://notificationServer.com?url={$request.body#/callbackUrl}&event={$request.body#/eventName}':
|
||||||
|
post:
|
||||||
|
deprecated: true
|
||||||
|
summary: Order delivered
|
||||||
|
description: A callback triggered every time an Order is delivered to the recipient
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
orderId:
|
||||||
|
type: string
|
||||||
|
example: '123'
|
||||||
|
timestamp:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
example: '2018-10-19T16:46:45Z'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Callback successfully processed and no retries will be performed
|
||||||
/user:
|
/user:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
|
@ -955,7 +1183,7 @@ components:
|
||||||
examples:
|
examples:
|
||||||
Order:
|
Order:
|
||||||
value:
|
value:
|
||||||
quantity: 1,
|
quantity: 1
|
||||||
shipDate: 2018-10-19T16:46:45Z,
|
shipDate: '2018-10-19T16:46:45Z'
|
||||||
status: placed,
|
status: placed
|
||||||
complete: false
|
complete: false
|
||||||
|
|
|
@ -6,7 +6,7 @@ describe('Menu', () => {
|
||||||
it('should have valid items count', () => {
|
it('should have valid items count', () => {
|
||||||
cy.get('.menu-content')
|
cy.get('.menu-content')
|
||||||
.find('li')
|
.find('li')
|
||||||
.should('have.length', 6 + (2 + 8 + 1 + 4 + 2) + (1 + 8));
|
.should('have.length', 6 + (2 + 8 + 1 + 4 + 2) + (1 + 8) + 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync active menu items while scroll', () => {
|
it('should sync active menu items while scroll', () => {
|
||||||
|
|
243
package-lock.json
generated
243
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"version": "2.0.0-rc.24",
|
"version": "2.0.0-rc.26",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -2867,14 +2867,26 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bl": {
|
"bl": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
|
||||||
"integrity": "sha512-FL/TdvchukRCuWVxT0YMO/7+L5TNeNrVFvRU2IY63aUyv9mpt8splf2NEr6qXtPo5fya5a66YohQKvGNmLrWNA==",
|
"integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"buffer": "^5.5.0",
|
||||||
|
"inherits": "^2.0.4",
|
||||||
"readable-stream": "^3.4.0"
|
"readable-stream": "^3.4.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"buffer": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.0.2",
|
||||||
|
"ieee754": "^1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||||
|
@ -7270,22 +7282,182 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"handlebars": {
|
"handlebars": {
|
||||||
"version": "4.7.3",
|
"version": "4.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.4.tgz",
|
||||||
"integrity": "sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg==",
|
"integrity": "sha512-Is8+SzHv8K9STNadlBVpVhxXrSXxVgTyIvhdg2Qjak1SfSZ7iEozLHdwiX1jJ9lLFkcFJxqGK5s/cI7ZX+qGkQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"neo-async": "^2.6.0",
|
"neo-async": "^2.6.0",
|
||||||
"optimist": "^0.6.1",
|
|
||||||
"source-map": "^0.6.1",
|
"source-map": "^0.6.1",
|
||||||
"uglify-js": "^3.1.4"
|
"uglify-js": "^3.1.4",
|
||||||
|
"yargs": "^15.3.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/color-name": "^1.1.1",
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cliui": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"wrap-ansi": "^6.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"get-caller-file": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-locate": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path-exists": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"require-main-filename": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"source-map": {
|
"source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wrap-ansi": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yargs": {
|
||||||
|
"version": "15.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
|
||||||
|
"integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"cliui": "^6.0.0",
|
||||||
|
"decamelize": "^1.2.0",
|
||||||
|
"find-up": "^4.1.0",
|
||||||
|
"get-caller-file": "^2.0.1",
|
||||||
|
"require-directory": "^2.1.1",
|
||||||
|
"require-main-filename": "^2.0.0",
|
||||||
|
"set-blocking": "^2.0.0",
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"which-module": "^2.0.0",
|
||||||
|
"y18n": "^4.0.0",
|
||||||
|
"yargs-parser": "^18.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yargs-parser": {
|
||||||
|
"version": "18.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz",
|
||||||
|
"integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"camelcase": "^5.0.0",
|
||||||
|
"decamelize": "^1.2.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9444,8 +9616,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
"resolved": "",
|
||||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
|
@ -11485,6 +11656,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mkdirp-classic": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz",
|
||||||
|
"integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"mobx": {
|
"mobx": {
|
||||||
"version": "4.15.4",
|
"version": "4.15.4",
|
||||||
"resolved": "https://registry.npmjs.org/mobx/-/mobx-4.15.4.tgz",
|
"resolved": "https://registry.npmjs.org/mobx/-/mobx-4.15.4.tgz",
|
||||||
|
@ -12098,24 +12275,6 @@
|
||||||
"is-wsl": "^1.1.0"
|
"is-wsl": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"optimist": {
|
|
||||||
"version": "0.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
|
|
||||||
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"minimist": "~0.0.1",
|
|
||||||
"wordwrap": "~0.0.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"minimist": {
|
|
||||||
"version": "0.0.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
|
|
||||||
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"optionator": {
|
"optionator": {
|
||||||
"version": "0.8.3",
|
"version": "0.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
|
||||||
|
@ -14939,13 +15098,13 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"tar-fs": {
|
"tar-fs": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
|
||||||
"integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==",
|
"integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"chownr": "^1.1.1",
|
"chownr": "^1.1.1",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp-classic": "^0.5.2",
|
||||||
"pump": "^3.0.0",
|
"pump": "^3.0.0",
|
||||||
"tar-stream": "^2.0.0"
|
"tar-stream": "^2.0.0"
|
||||||
}
|
}
|
||||||
|
@ -15412,9 +15571,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uglify-js": {
|
"uglify-js": {
|
||||||
"version": "3.8.0",
|
"version": "3.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz",
|
||||||
"integrity": "sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ==",
|
"integrity": "sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -16308,8 +16467,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
"resolved": "",
|
||||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
|
@ -17407,8 +17565,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
"resolved": "",
|
||||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
|
@ -17776,12 +17933,6 @@
|
||||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"wordwrap": {
|
|
||||||
"version": "0.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
|
|
||||||
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"worker-farm": {
|
"worker-farm": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
|
||||||
|
|
35
src/components/CallbackSamples/CallbackReqSamples.tsx
Normal file
35
src/components/CallbackSamples/CallbackReqSamples.tsx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
import { DropdownProps } from '../../common-elements';
|
||||||
|
import { PayloadSamples } from '../PayloadSamples/PayloadSamples';
|
||||||
|
import { OperationModel } from '../../services/models';
|
||||||
|
import { XPayloadSample } from '../../services/models/Operation';
|
||||||
|
import { isPayloadSample } from '../../services';
|
||||||
|
|
||||||
|
export interface PayloadSampleProps {
|
||||||
|
callback: OperationModel;
|
||||||
|
renderDropdown: (props: DropdownProps) => JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CallbackPayloadSample extends React.Component<PayloadSampleProps> {
|
||||||
|
render() {
|
||||||
|
const payloadSample = this.props.callback.codeSamples.find(sample =>
|
||||||
|
isPayloadSample(sample),
|
||||||
|
) as XPayloadSample | undefined;
|
||||||
|
|
||||||
|
if (!payloadSample) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PayloadSampleWrapper>
|
||||||
|
<PayloadSamples content={payloadSample.requestBodyContent} />
|
||||||
|
</PayloadSampleWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PayloadSampleWrapper = styled.div`
|
||||||
|
margin-top: 15px;
|
||||||
|
`;
|
79
src/components/CallbackSamples/CallbackSamples.tsx
Normal file
79
src/components/CallbackSamples/CallbackSamples.tsx
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
import { RightPanelHeader } from '../../common-elements';
|
||||||
|
import { RedocNormalizedOptions } from '../../services';
|
||||||
|
import { CallbackModel } from '../../services/models';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
|
import { GenericChildrenSwitcher } from '../GenericChildrenSwitcher/GenericChildrenSwitcher';
|
||||||
|
import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel';
|
||||||
|
import { InvertedSimpleDropdown, MimeLabel } from '../PayloadSamples/styled.elements';
|
||||||
|
import { CallbackPayloadSample } from './CallbackReqSamples';
|
||||||
|
|
||||||
|
export interface CallbackSamplesProps {
|
||||||
|
callbacks: CallbackModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class CallbackSamples extends React.Component<CallbackSamplesProps> {
|
||||||
|
static contextType = OptionsContext;
|
||||||
|
context: RedocNormalizedOptions;
|
||||||
|
|
||||||
|
private renderDropdown = props => {
|
||||||
|
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { callbacks } = this.props;
|
||||||
|
|
||||||
|
if (!callbacks || callbacks.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const operations = callbacks
|
||||||
|
.map(callback => callback.operations.map(operation => operation))
|
||||||
|
.reduce((a, b) => a.concat(b), []);
|
||||||
|
|
||||||
|
const hasSamples = operations.some(operation => operation.codeSamples.length > 0);
|
||||||
|
|
||||||
|
if (!hasSamples) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dropdownOptions = operations.map((callback, idx) => {
|
||||||
|
return {
|
||||||
|
label: `${callback.httpVerb.toUpperCase()}: ${callback.name}`,
|
||||||
|
value: idx.toString(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<RightPanelHeader> Callback payload samples </RightPanelHeader>
|
||||||
|
|
||||||
|
<SamplesWrapper>
|
||||||
|
<GenericChildrenSwitcher
|
||||||
|
items={operations}
|
||||||
|
renderDropdown={this.renderDropdown}
|
||||||
|
label={'Callback'}
|
||||||
|
options={dropdownOptions}
|
||||||
|
>
|
||||||
|
{callback => (
|
||||||
|
<CallbackPayloadSample
|
||||||
|
key="callbackPayloadSample"
|
||||||
|
callback={callback}
|
||||||
|
renderDropdown={this.renderDropdown}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</GenericChildrenSwitcher>
|
||||||
|
</SamplesWrapper>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SamplesWrapper = styled.div`
|
||||||
|
background: ${({ theme }) => theme.codeBlock.backgroundColor};
|
||||||
|
padding: ${props => props.theme.spacing.unit * 4}px;
|
||||||
|
`;
|
46
src/components/Callbacks/CallbackDetails.tsx
Normal file
46
src/components/Callbacks/CallbackDetails.tsx
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { OperationModel } from '../../services/models';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
import { Endpoint } from '../Endpoint/Endpoint';
|
||||||
|
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||||
|
import { Extensions } from '../Fields/Extensions';
|
||||||
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
|
import { Parameters } from '../Parameters/Parameters';
|
||||||
|
import { ResponsesList } from '../Responses/ResponsesList';
|
||||||
|
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
|
||||||
|
import { CallbackDetailsWrap } from './styled.elements';
|
||||||
|
|
||||||
|
export interface CallbackDetailsProps {
|
||||||
|
operation: OperationModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class CallbackDetails extends React.Component<CallbackDetailsProps> {
|
||||||
|
render() {
|
||||||
|
const { operation } = this.props;
|
||||||
|
const { description, externalDocs } = operation;
|
||||||
|
const hasDescription = !!(description || externalDocs);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CallbackDetailsWrap>
|
||||||
|
{hasDescription && (
|
||||||
|
<Description>
|
||||||
|
{description !== undefined && <Markdown source={description} />}
|
||||||
|
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
|
||||||
|
</Description>
|
||||||
|
)}
|
||||||
|
<Endpoint operation={this.props.operation} inverted={true} compact={true} />
|
||||||
|
<Extensions extensions={operation.extensions} />
|
||||||
|
<SecurityRequirements securities={operation.security} />
|
||||||
|
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
||||||
|
<ResponsesList responses={operation.responses} isCallback={operation.isCallback} />
|
||||||
|
</CallbackDetailsWrap>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Description = styled.div`
|
||||||
|
margin-bottom: ${({ theme }) => theme.spacing.unit * 3}px;
|
||||||
|
`;
|
30
src/components/Callbacks/CallbackOperation.tsx
Normal file
30
src/components/Callbacks/CallbackOperation.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { OperationModel } from '../../services/models';
|
||||||
|
import { StyledCallbackTitle } from './styled.elements';
|
||||||
|
import { CallbackDetails } from './CallbackDetails';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class CallbackOperation extends React.Component<{ callbackOperation: OperationModel }> {
|
||||||
|
toggle = () => {
|
||||||
|
this.props.callbackOperation.toggle();
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { name, expanded, httpVerb, deprecated } = this.props.callbackOperation;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StyledCallbackTitle
|
||||||
|
onClick={this.toggle}
|
||||||
|
name={name}
|
||||||
|
opened={expanded}
|
||||||
|
httpVerb={httpVerb}
|
||||||
|
deprecated={deprecated}
|
||||||
|
/>
|
||||||
|
{expanded && <CallbackDetails operation={this.props.callbackOperation} />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
54
src/components/Callbacks/CallbackTitle.tsx
Normal file
54
src/components/Callbacks/CallbackTitle.tsx
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { darken } from 'polished';
|
||||||
|
import { ShelfIcon } from '../../common-elements';
|
||||||
|
import { OperationBadge } from '../SideMenu/styled.elements';
|
||||||
|
import { shortenHTTPVerb } from '../../utils/openapi';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
import { Badge } from '../../common-elements/';
|
||||||
|
import { l } from '../../services/Labels';
|
||||||
|
|
||||||
|
export interface CallbackTitleProps {
|
||||||
|
name: string;
|
||||||
|
opened?: boolean;
|
||||||
|
httpVerb: string;
|
||||||
|
deprecated?: boolean;
|
||||||
|
className?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CallbackTitle extends React.PureComponent<CallbackTitleProps> {
|
||||||
|
render() {
|
||||||
|
const { name, opened, className, onClick, httpVerb, deprecated } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CallbackTitleWrapper className={className} onClick={onClick || undefined}>
|
||||||
|
<OperationBadgeStyled type={httpVerb}>{shortenHTTPVerb(httpVerb)}</OperationBadgeStyled>
|
||||||
|
<ShelfIcon size={'1.5em'} direction={opened ? 'down' : 'right'} float={'left'} />
|
||||||
|
<CallbackName deprecated={deprecated}>{name}</CallbackName>
|
||||||
|
{deprecated ? <Badge type="warning"> {l('deprecated')} </Badge> : null}
|
||||||
|
</CallbackTitleWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CallbackTitleWrapper = styled.div`
|
||||||
|
& > * {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
${ShelfIcon} {
|
||||||
|
polygon {
|
||||||
|
fill: ${({ theme }) => darken(theme.colors.tonalOffset, theme.colors.gray[100])};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CallbackName = styled.span<{ deprecated?: boolean }>`
|
||||||
|
text-decoration: ${props => (props.deprecated ? 'line-through' : 'none')};
|
||||||
|
margin-right: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const OperationBadgeStyled = styled(OperationBadge)`
|
||||||
|
margin: 0px 5px 0px 0px;
|
||||||
|
`;
|
40
src/components/Callbacks/CallbacksList.tsx
Normal file
40
src/components/Callbacks/CallbacksList.tsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { CallbackModel } from '../../services/models';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
import { CallbackOperation } from './CallbackOperation';
|
||||||
|
|
||||||
|
export interface CallbacksListProps {
|
||||||
|
callbacks: CallbackModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CallbacksList extends React.PureComponent<CallbacksListProps> {
|
||||||
|
render() {
|
||||||
|
const { callbacks } = this.props;
|
||||||
|
|
||||||
|
if (!callbacks || callbacks.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<CallbacksHeader> Callbacks </CallbacksHeader>
|
||||||
|
{callbacks.map(callback => {
|
||||||
|
return callback.operations.map((operation, index) => {
|
||||||
|
return (
|
||||||
|
<CallbackOperation key={`${callback.name}_${index}`} callbackOperation={operation} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CallbacksHeader = styled.h3`
|
||||||
|
font-size: 1.3em;
|
||||||
|
padding: 0.2em 0;
|
||||||
|
margin: 3em 0 1.1em;
|
||||||
|
color: ${({ theme }) => theme.colors.text.primary};
|
||||||
|
font-weight: normal;
|
||||||
|
`;
|
3
src/components/Callbacks/index.ts
Normal file
3
src/components/Callbacks/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export * from './CallbackOperation';
|
||||||
|
export * from './CallbackTitle';
|
||||||
|
export * from './CallbacksList';
|
18
src/components/Callbacks/styled.elements.ts
Normal file
18
src/components/Callbacks/styled.elements.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
import { CallbackTitle } from './CallbackTitle';
|
||||||
|
|
||||||
|
export const StyledCallbackTitle = styled(CallbackTitle)`
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
line-height: 1.5em;
|
||||||
|
background-color: ${({ theme }) => theme.colors.gray[100]};
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CallbackDetailsWrap = styled.div`
|
||||||
|
padding: 10px 25px;
|
||||||
|
background-color: ${({ theme }) => theme.colors.gray[50]};
|
||||||
|
margin-bottom: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
`;
|
|
@ -3,7 +3,6 @@ import * as React from 'react';
|
||||||
|
|
||||||
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||||
import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown';
|
import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown';
|
||||||
|
|
||||||
import { H1, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
import { H1, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
||||||
import { ContentItemModel } from '../../services/MenuBuilder';
|
import { ContentItemModel } from '../../services/MenuBuilder';
|
||||||
import { GroupModel, OperationModel } from '../../services/models';
|
import { GroupModel, OperationModel } from '../../services/models';
|
||||||
|
@ -18,7 +17,9 @@ export class ContentItems extends React.Component<{
|
||||||
if (items.length === 0) {
|
if (items.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return items.map(item => <ContentItem item={item} key={item.id} />);
|
return items.map(item => {
|
||||||
|
return <ContentItem key={item.id} item={item} />;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ export interface EndpointProps {
|
||||||
|
|
||||||
hideHostname?: boolean;
|
hideHostname?: boolean;
|
||||||
inverted?: boolean;
|
inverted?: boolean;
|
||||||
|
compact?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EndpointState {
|
export interface EndpointState {
|
||||||
|
@ -49,7 +50,9 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
||||||
{options => (
|
{options => (
|
||||||
<OperationEndpointWrap>
|
<OperationEndpointWrap>
|
||||||
<EndpointInfo onClick={this.toggle} expanded={expanded} inverted={inverted}>
|
<EndpointInfo onClick={this.toggle} expanded={expanded} inverted={inverted}>
|
||||||
<HttpVerb type={operation.httpVerb}> {operation.httpVerb}</HttpVerb>{' '}
|
<HttpVerb type={operation.httpVerb} compact={this.props.compact}>
|
||||||
|
{operation.httpVerb}
|
||||||
|
</HttpVerb>
|
||||||
<ServerRelativeURL>{operation.path}</ServerRelativeURL>
|
<ServerRelativeURL>{operation.path}</ServerRelativeURL>
|
||||||
<ShelfIcon
|
<ShelfIcon
|
||||||
float={'right'}
|
float={'right'}
|
||||||
|
|
|
@ -34,14 +34,14 @@ export const EndpointInfo = styled.div<{ expanded?: boolean; inverted?: boolean
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const HttpVerb = styled.span.attrs((props: { type: string }) => ({
|
export const HttpVerb = styled.span.attrs((props: { type: string; compact?: boolean }) => ({
|
||||||
className: `http-verb ${props.type}`,
|
className: `http-verb ${props.type}`,
|
||||||
}))<{ type: string }>`
|
}))<{ type: string; compact?: boolean }>`
|
||||||
font-size: 0.929em;
|
font-size: ${props => (props.compact ? '0.8em' : '0.929em')};
|
||||||
line-height: 20px;
|
line-height: ${props => (props.compact ? '18px' : '20px')};
|
||||||
background-color: ${(props: any) => props.theme.colors.http[props.type] || '#999999'};
|
background-color: ${props => props.theme.colors.http[props.type] || '#999999'};
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
padding: 3px 10px;
|
padding: ${props => (props.compact ? '2px 8px' : '3px 10px')};
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -59,7 +59,6 @@ export const ServersOverlay = styled.div<{ expanded: boolean }>`
|
||||||
border-bottom-left-radius: 4px;
|
border-bottom-left-radius: 4px;
|
||||||
border-bottom-right-radius: 4px;
|
border-bottom-right-radius: 4px;
|
||||||
transition: all 0.25s ease;
|
transition: all 0.25s ease;
|
||||||
|
|
||||||
${props => (props.expanded ? '' : 'transform: translateY(-50%) scaleY(0);')}
|
${props => (props.expanded ? '' : 'transform: translateY(-50%) scaleY(0);')}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { DropdownProps, DropdownOption } from '../../common-elements/dropdown';
|
||||||
|
import { DropdownLabel, DropdownWrapper } from '../PayloadSamples/styled.elements';
|
||||||
|
|
||||||
|
export interface GenericChildrenSwitcherProps<T> {
|
||||||
|
items?: T[];
|
||||||
|
options: DropdownOption[];
|
||||||
|
label?: string;
|
||||||
|
renderDropdown: (props: DropdownProps) => JSX.Element;
|
||||||
|
children: (activeItem: T) => JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenericChildrenSwitcherState {
|
||||||
|
activeItemIdx: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO: Refactor this component:
|
||||||
|
* Implement rendering dropdown/label directly in this component
|
||||||
|
* Accept as a parameter mapper-function for building dropdown option labels
|
||||||
|
*/
|
||||||
|
@observer
|
||||||
|
export class GenericChildrenSwitcher<T> extends React.Component<
|
||||||
|
GenericChildrenSwitcherProps<T>,
|
||||||
|
GenericChildrenSwitcherState
|
||||||
|
> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
activeItemIdx: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
switchItem = ({ value }) => {
|
||||||
|
if (this.props.items) {
|
||||||
|
this.setState({
|
||||||
|
activeItemIdx: parseInt(value, 10),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { items } = this.props;
|
||||||
|
|
||||||
|
if (!items || !items.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = ({ children }) =>
|
||||||
|
this.props.label ? (
|
||||||
|
<DropdownWrapper>
|
||||||
|
<DropdownLabel>{this.props.label}</DropdownLabel>
|
||||||
|
{children}
|
||||||
|
</DropdownWrapper>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Wrapper>
|
||||||
|
{this.props.renderDropdown({
|
||||||
|
value: this.props.options[this.state.activeItemIdx],
|
||||||
|
options: this.props.options,
|
||||||
|
onChange: this.switchItem,
|
||||||
|
})}
|
||||||
|
</Wrapper>
|
||||||
|
|
||||||
|
{this.props.children(items[this.state.activeItemIdx])}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,29 +1,26 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
|
|
||||||
|
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
import { Badge, DarkRightPanel, H2, MiddlePanel, Row } from '../../common-elements';
|
import { Badge, DarkRightPanel, H2, MiddlePanel, Row } from '../../common-elements';
|
||||||
|
|
||||||
import { OptionsContext } from '../OptionsProvider';
|
|
||||||
|
|
||||||
import { ShareLink } from '../../common-elements/linkify';
|
import { ShareLink } from '../../common-elements/linkify';
|
||||||
|
import { OperationModel } from '../../services/models';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
import { CallbacksList } from '../Callbacks';
|
||||||
|
import { CallbackSamples } from '../CallbackSamples/CallbackSamples';
|
||||||
import { Endpoint } from '../Endpoint/Endpoint';
|
import { Endpoint } from '../Endpoint/Endpoint';
|
||||||
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||||
|
import { Extensions } from '../Fields/Extensions';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
import { Parameters } from '../Parameters/Parameters';
|
import { Parameters } from '../Parameters/Parameters';
|
||||||
import { RequestSamples } from '../RequestSamples/RequestSamples';
|
import { RequestSamples } from '../RequestSamples/RequestSamples';
|
||||||
import { ResponsesList } from '../Responses/ResponsesList';
|
import { ResponsesList } from '../Responses/ResponsesList';
|
||||||
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
||||||
|
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
|
||||||
import { OperationModel as OperationType } from '../../services/models';
|
|
||||||
import styled from '../../styled-components';
|
|
||||||
import { Extensions } from '../Fields/Extensions';
|
|
||||||
|
|
||||||
const OperationRow = styled(Row)`
|
const OperationRow = styled(Row)`
|
||||||
backface-visibility: hidden;
|
backface-visibility: hidden;
|
||||||
contain: content;
|
contain: content;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -32,7 +29,7 @@ const Description = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export interface OperationProps {
|
export interface OperationProps {
|
||||||
operation: OperationType;
|
operation: OperationModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
|
@ -63,11 +60,13 @@ export class Operation extends React.Component<OperationProps> {
|
||||||
<SecurityRequirements securities={operation.security} />
|
<SecurityRequirements securities={operation.security} />
|
||||||
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
||||||
<ResponsesList responses={operation.responses} />
|
<ResponsesList responses={operation.responses} />
|
||||||
|
<CallbacksList callbacks={operation.callbacks} />
|
||||||
</MiddlePanel>
|
</MiddlePanel>
|
||||||
<DarkRightPanel>
|
<DarkRightPanel>
|
||||||
{!options.pathInMiddlePanel && <Endpoint operation={operation} />}
|
{!options.pathInMiddlePanel && <Endpoint operation={operation} />}
|
||||||
<RequestSamples operation={operation} />
|
<RequestSamples operation={operation} />
|
||||||
<ResponseSamples operation={operation} />
|
<ResponseSamples operation={operation} />
|
||||||
|
<CallbackSamples callbacks={operation.callbacks} />
|
||||||
</DarkRightPanel>
|
</DarkRightPanel>
|
||||||
</OperationRow>
|
</OperationRow>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -4,14 +4,16 @@ import ReactDropdown from 'react-dropdown';
|
||||||
|
|
||||||
import { transparentize } from 'polished';
|
import { transparentize } from 'polished';
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
import { StyledDropdown } from '../../common-elements';
|
import { StyledDropdown } from '../../common-elements';
|
||||||
|
|
||||||
export const MimeLabel = styled.div`
|
export const MimeLabel = styled.div`
|
||||||
padding: 12px;
|
padding: 0.9em;
|
||||||
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
display: block;
|
display: block;
|
||||||
|
font-family: ${({ theme }) => theme.typography.headings.fontFamily};
|
||||||
|
font-size: 0.929em;
|
||||||
|
line-height: 1.5em;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const DropdownLabel = styled.span`
|
export const DropdownLabel = styled.span`
|
||||||
|
@ -36,6 +38,11 @@ export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
display: block;
|
display: block;
|
||||||
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
||||||
|
.Dropdown-placeholder {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
.Dropdown-control {
|
.Dropdown-control {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
@ -55,6 +62,11 @@ export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
||||||
.Dropdown-menu {
|
.Dropdown-menu {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
.Dropdown-option {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -4,20 +4,21 @@ import styled from '../../styled-components';
|
||||||
import { ResponseView } from './Response';
|
import { ResponseView } from './Response';
|
||||||
|
|
||||||
const ResponsesHeader = styled.h3`
|
const ResponsesHeader = styled.h3`
|
||||||
font-size: 18px;
|
font-size: 1.3em;
|
||||||
padding: 0.2em 0;
|
padding: 0.2em 0;
|
||||||
margin: 3em 0 1.1em;
|
margin: 3em 0 1.1em;
|
||||||
color: #253137;
|
color: ${({ theme }) => theme.colors.text.primary};
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export interface ResponseListProps {
|
export interface ResponseListProps {
|
||||||
responses: ResponseModel[];
|
responses: ResponseModel[];
|
||||||
|
isCallback?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ResponsesList extends React.PureComponent<ResponseListProps> {
|
export class ResponsesList extends React.PureComponent<ResponseListProps> {
|
||||||
render() {
|
render() {
|
||||||
const { responses } = this.props;
|
const { responses, isCallback } = this.props;
|
||||||
|
|
||||||
if (!responses || responses.length === 0) {
|
if (!responses || responses.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -25,7 +26,7 @@ export class ResponsesList extends React.PureComponent<ResponseListProps> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ResponsesHeader> Responses </ResponsesHeader>
|
<ResponsesHeader>{isCallback ? 'Callback responses' : 'Responses'}</ResponsesHeader>
|
||||||
{responses.map(response => {
|
{responses.map(response => {
|
||||||
return <ResponseView key={response.code} response={response} />;
|
return <ResponseView key={response.code} response={response} />;
|
||||||
})}
|
})}
|
||||||
|
|
59
src/components/__tests__/Callbacks.test.tsx
Normal file
59
src/components/__tests__/Callbacks.test.tsx
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/* tslint:disable:no-implicit-dependencies */
|
||||||
|
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { OpenAPIParser } from '../../services';
|
||||||
|
import { CallbackModel } from '../../services/models/Callback';
|
||||||
|
import { RedocNormalizedOptions } from '../../services/RedocNormalizedOptions';
|
||||||
|
import { CallbacksList, CallbackTitle, CallbackOperation } from '../Callbacks';
|
||||||
|
import * as simpleCallbackFixture from './fixtures/simple-callback.json';
|
||||||
|
|
||||||
|
const options = new RedocNormalizedOptions({});
|
||||||
|
describe('Components', () => {
|
||||||
|
describe('Callbacks', () => {
|
||||||
|
it('should correctly render CallbackView', () => {
|
||||||
|
const parser = new OpenAPIParser(simpleCallbackFixture, undefined, options);
|
||||||
|
const callback = new CallbackModel(
|
||||||
|
parser,
|
||||||
|
'Test.Callback',
|
||||||
|
{ $ref: '#/components/callbacks/Test' },
|
||||||
|
'',
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
// There should be 1 operation defined in simple-callback.json, just get it manually for readability.
|
||||||
|
const callbackViewElement = shallow(
|
||||||
|
<CallbackOperation key={callback.name} callbackOperation={callback.operations[0]} />,
|
||||||
|
).getElement();
|
||||||
|
expect(callbackViewElement.props).toBeDefined();
|
||||||
|
expect(callbackViewElement.props.children).toBeDefined();
|
||||||
|
expect(callbackViewElement.props.children.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly render CallbackTitle', () => {
|
||||||
|
const callbackTitleViewElement = shallow(
|
||||||
|
<CallbackTitle name={'Test'} className={'.test'} onClick={undefined} httpVerb={'get'} />,
|
||||||
|
).getElement();
|
||||||
|
expect(callbackTitleViewElement.props).toBeDefined();
|
||||||
|
expect(callbackTitleViewElement.props.className).toEqual('.test');
|
||||||
|
expect(callbackTitleViewElement.props.onClick).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly render CallbacksList', () => {
|
||||||
|
const parser = new OpenAPIParser(simpleCallbackFixture, undefined, options);
|
||||||
|
const callback = new CallbackModel(
|
||||||
|
parser,
|
||||||
|
'Test.Callback',
|
||||||
|
{ $ref: '#/components/callbacks/Test' },
|
||||||
|
'',
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
const callbacksListViewElement = shallow(
|
||||||
|
<CallbacksList callbacks={[callback]} />,
|
||||||
|
).getElement();
|
||||||
|
expect(callbacksListViewElement.props).toBeDefined();
|
||||||
|
expect(callbacksListViewElement.props.children).toBeDefined();
|
||||||
|
expect(callbacksListViewElement.props.children.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
66
src/components/__tests__/fixtures/simple-callback.json
Normal file
66
src/components/__tests__/fixtures/simple-callback.json
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
{
|
||||||
|
"openapi": "3.0.0",
|
||||||
|
"info": {
|
||||||
|
"version": "1.0",
|
||||||
|
"title": "Foo"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"callbacks": {
|
||||||
|
"Test": {
|
||||||
|
"/test": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "testCallback",
|
||||||
|
"description": "Test callback.",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"title": "TestTitle",
|
||||||
|
"type": "object",
|
||||||
|
"description": "Test description",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The type of response.",
|
||||||
|
"enum": [
|
||||||
|
"TestResponse.Complete"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"FAILURE",
|
||||||
|
"SUCCESS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"status"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "X-Test-Header",
|
||||||
|
"in": "header",
|
||||||
|
"required": true,
|
||||||
|
"example": "1",
|
||||||
|
"description": "This is a test header parameter",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "Test response."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,16 @@
|
||||||
import { OpenAPIOperation, OpenAPIParameter, OpenAPISpec, OpenAPITag, Referenced } from '../types';
|
import {
|
||||||
|
OpenAPIOperation,
|
||||||
|
OpenAPIParameter,
|
||||||
|
OpenAPISpec,
|
||||||
|
OpenAPITag,
|
||||||
|
Referenced,
|
||||||
|
OpenAPIServer,
|
||||||
|
} from '../types';
|
||||||
import {
|
import {
|
||||||
isOperationName,
|
isOperationName,
|
||||||
SECURITY_DEFINITIONS_COMPONENT_NAME,
|
SECURITY_DEFINITIONS_COMPONENT_NAME,
|
||||||
setSecuritySchemePrefix,
|
setSecuritySchemePrefix,
|
||||||
|
JsonPointer,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { MarkdownRenderer } from './MarkdownRenderer';
|
import { MarkdownRenderer } from './MarkdownRenderer';
|
||||||
import { GroupModel, OperationModel } from './models';
|
import { GroupModel, OperationModel } from './models';
|
||||||
|
@ -15,9 +23,11 @@ export type TagInfo = OpenAPITag & {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ExtendedOpenAPIOperation = {
|
export type ExtendedOpenAPIOperation = {
|
||||||
|
pointer: string;
|
||||||
pathName: string;
|
pathName: string;
|
||||||
httpVerb: string;
|
httpVerb: string;
|
||||||
pathParameters: Array<Referenced<OpenAPIParameter>>;
|
pathParameters: Array<Referenced<OpenAPIParameter>>;
|
||||||
|
pathServers: Array<OpenAPIServer> | undefined;
|
||||||
} & OpenAPIOperation;
|
} & OpenAPIOperation;
|
||||||
|
|
||||||
export type TagsInfoMap = Dict<TagInfo>;
|
export type TagsInfoMap = Dict<TagInfo>;
|
||||||
|
@ -237,8 +247,10 @@ export class MenuBuilder {
|
||||||
tag.operations.push({
|
tag.operations.push({
|
||||||
...operationInfo,
|
...operationInfo,
|
||||||
pathName,
|
pathName,
|
||||||
|
pointer: JsonPointer.compile(['paths', pathName, operationName]),
|
||||||
httpVerb: operationName,
|
httpVerb: operationName,
|
||||||
pathParameters: path.parameters || [],
|
pathParameters: path.parameters || [],
|
||||||
|
pathServers: path.servers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
64
src/services/__tests__/fixtures/callback.json
Normal file
64
src/services/__tests__/fixtures/callback.json
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"openapi": "3.0.0",
|
||||||
|
"info": {
|
||||||
|
"version": "1.0",
|
||||||
|
"title": "Foo"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"callbacks": {
|
||||||
|
"Test": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "testCallback",
|
||||||
|
"description": "Test callback.",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"title": "TestTitle",
|
||||||
|
"type": "object",
|
||||||
|
"description": "Test description",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The type of response.",
|
||||||
|
"enum": [
|
||||||
|
"TestResponse.Complete"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"FAILURE",
|
||||||
|
"SUCCESS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"status"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "X-Test-Header",
|
||||||
|
"in": "header",
|
||||||
|
"required": true,
|
||||||
|
"example": "1",
|
||||||
|
"description": "This is a test header parameter",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "Test response."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/services/__tests__/models/Callback.test.ts
Normal file
26
src/services/__tests__/models/Callback.test.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { CallbackModel } from '../../models/Callback';
|
||||||
|
import { OpenAPIParser } from '../../OpenAPIParser';
|
||||||
|
import { RedocNormalizedOptions } from '../../RedocNormalizedOptions';
|
||||||
|
|
||||||
|
const opts = new RedocNormalizedOptions({});
|
||||||
|
|
||||||
|
describe('Models', () => {
|
||||||
|
describe('CallbackModel', () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const spec = require('../fixtures/callback.json');
|
||||||
|
const parser = new OpenAPIParser(spec, undefined, opts);
|
||||||
|
|
||||||
|
test('basic callback details', () => {
|
||||||
|
const callback = new CallbackModel(
|
||||||
|
parser,
|
||||||
|
'Test.Callback',
|
||||||
|
{ $ref: '#/components/callbacks/Test' },
|
||||||
|
'',
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
expect(callback.name).toEqual('Test.Callback');
|
||||||
|
expect(callback.operations.length).toEqual(0);
|
||||||
|
expect(callback.expanded).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
56
src/services/models/Callback.ts
Normal file
56
src/services/models/Callback.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { action, observable } from 'mobx';
|
||||||
|
|
||||||
|
import { OpenAPICallback, Referenced } from '../../types';
|
||||||
|
import { isOperationName, JsonPointer } from '../../utils';
|
||||||
|
import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
|
import { OperationModel } from './Operation';
|
||||||
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
|
|
||||||
|
export class CallbackModel {
|
||||||
|
@observable
|
||||||
|
expanded: boolean;
|
||||||
|
name: string;
|
||||||
|
operations: OperationModel[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
parser: OpenAPIParser,
|
||||||
|
name: string,
|
||||||
|
infoOrRef: Referenced<OpenAPICallback>,
|
||||||
|
pointer: string,
|
||||||
|
options: RedocNormalizedOptions,
|
||||||
|
) {
|
||||||
|
this.name = name;
|
||||||
|
const paths = parser.deref<OpenAPICallback>(infoOrRef);
|
||||||
|
parser.exitRef(infoOrRef);
|
||||||
|
|
||||||
|
for (const pathName of Object.keys(paths)) {
|
||||||
|
const path = paths[pathName];
|
||||||
|
const operations = Object.keys(path).filter(isOperationName);
|
||||||
|
for (const operationName of operations) {
|
||||||
|
const operationInfo = path[operationName];
|
||||||
|
|
||||||
|
const operation = new OperationModel(
|
||||||
|
parser,
|
||||||
|
{
|
||||||
|
...operationInfo,
|
||||||
|
pathName,
|
||||||
|
pointer: JsonPointer.compile([pointer, name, pathName, operationName]),
|
||||||
|
httpVerb: operationName,
|
||||||
|
pathParameters: path.parameters || [],
|
||||||
|
pathServers: path.servers,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
options,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.operations.push(operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggle() {
|
||||||
|
this.expanded = !this.expanded;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,19 +4,13 @@ import { IMenuItem } from '../MenuStore';
|
||||||
import { GroupModel } from './Group.model';
|
import { GroupModel } from './Group.model';
|
||||||
import { SecurityRequirementModel } from './SecurityRequirement';
|
import { SecurityRequirementModel } from './SecurityRequirement';
|
||||||
|
|
||||||
import {
|
import { OpenAPIExternalDocumentation, OpenAPIServer, OpenAPIXCodeSample } from '../../types';
|
||||||
OpenAPIExternalDocumentation,
|
|
||||||
OpenAPIPath,
|
|
||||||
OpenAPIServer,
|
|
||||||
OpenAPIXCodeSample,
|
|
||||||
} from '../../types';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
extractExtensions,
|
extractExtensions,
|
||||||
getOperationSummary,
|
getOperationSummary,
|
||||||
getStatusCodeType,
|
getStatusCodeType,
|
||||||
isStatusCode,
|
isStatusCode,
|
||||||
JsonPointer,
|
|
||||||
memoize,
|
memoize,
|
||||||
mergeParams,
|
mergeParams,
|
||||||
normalizeServers,
|
normalizeServers,
|
||||||
|
@ -26,12 +20,13 @@ import {
|
||||||
import { ContentItemModel, ExtendedOpenAPIOperation } from '../MenuBuilder';
|
import { ContentItemModel, ExtendedOpenAPIOperation } from '../MenuBuilder';
|
||||||
import { OpenAPIParser } from '../OpenAPIParser';
|
import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
|
import { CallbackModel } from './Callback';
|
||||||
import { FieldModel } from './Field';
|
import { FieldModel } from './Field';
|
||||||
import { MediaContentModel } from './MediaContent';
|
import { MediaContentModel } from './MediaContent';
|
||||||
import { RequestBodyModel } from './RequestBody';
|
import { RequestBodyModel } from './RequestBody';
|
||||||
import { ResponseModel } from './Response';
|
import { ResponseModel } from './Response';
|
||||||
|
|
||||||
interface XPayloadSample {
|
export interface XPayloadSample {
|
||||||
lang: 'payload';
|
lang: 'payload';
|
||||||
label: string;
|
label: string;
|
||||||
requestBodyContent: MediaContentModel;
|
requestBodyContent: MediaContentModel;
|
||||||
|
@ -77,23 +72,17 @@ export class OperationModel implements IMenuItem {
|
||||||
servers: OpenAPIServer[];
|
servers: OpenAPIServer[];
|
||||||
security: SecurityRequirementModel[];
|
security: SecurityRequirementModel[];
|
||||||
extensions: Dict<any>;
|
extensions: Dict<any>;
|
||||||
|
isCallback: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private parser: OpenAPIParser,
|
private parser: OpenAPIParser,
|
||||||
private operationSpec: ExtendedOpenAPIOperation,
|
private operationSpec: ExtendedOpenAPIOperation,
|
||||||
parent: GroupModel | undefined,
|
parent: GroupModel | undefined,
|
||||||
private options: RedocNormalizedOptions,
|
private options: RedocNormalizedOptions,
|
||||||
|
isCallback: boolean = false,
|
||||||
) {
|
) {
|
||||||
this.pointer = JsonPointer.compile(['paths', operationSpec.pathName, operationSpec.httpVerb]);
|
this.pointer = operationSpec.pointer;
|
||||||
|
|
||||||
this.id =
|
|
||||||
operationSpec.operationId !== undefined
|
|
||||||
? 'operation/' + operationSpec.operationId
|
|
||||||
: parent !== undefined
|
|
||||||
? parent.id + this.pointer
|
|
||||||
: this.pointer;
|
|
||||||
|
|
||||||
this.name = getOperationSummary(operationSpec);
|
|
||||||
this.description = operationSpec.description;
|
this.description = operationSpec.description;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.externalDocs = operationSpec.externalDocs;
|
this.externalDocs = operationSpec.externalDocs;
|
||||||
|
@ -103,19 +92,36 @@ export class OperationModel implements IMenuItem {
|
||||||
this.deprecated = !!operationSpec.deprecated;
|
this.deprecated = !!operationSpec.deprecated;
|
||||||
this.operationId = operationSpec.operationId;
|
this.operationId = operationSpec.operationId;
|
||||||
this.path = operationSpec.pathName;
|
this.path = operationSpec.pathName;
|
||||||
|
this.isCallback = isCallback;
|
||||||
|
|
||||||
const pathInfo = parser.byRef<OpenAPIPath>(
|
this.name = getOperationSummary(operationSpec);
|
||||||
JsonPointer.compile(['paths', operationSpec.pathName]),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.servers = normalizeServers(
|
if (this.isCallback) {
|
||||||
parser.specUrl,
|
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.
|
||||||
operationSpec.servers || (pathInfo && pathInfo.servers) || parser.spec.servers || [],
|
// Can be defined individually per-callback in the specification. Defaults to none.
|
||||||
);
|
this.security = (operationSpec.security || []).map(
|
||||||
|
security => new SecurityRequirementModel(security, parser),
|
||||||
|
);
|
||||||
|
|
||||||
this.security = (operationSpec.security || parser.spec.security || []).map(
|
// TODO: update getting pathInfo for overriding servers on path level
|
||||||
security => new SecurityRequirementModel(security, parser),
|
this.servers = normalizeServers('', operationSpec.servers || operationSpec.pathServers || []);
|
||||||
);
|
} else {
|
||||||
|
this.id =
|
||||||
|
operationSpec.operationId !== undefined
|
||||||
|
? 'operation/' + operationSpec.operationId
|
||||||
|
: parent !== undefined
|
||||||
|
? parent.id + this.pointer
|
||||||
|
: this.pointer;
|
||||||
|
|
||||||
|
this.security = (operationSpec.security || parser.spec.security || []).map(
|
||||||
|
security => new SecurityRequirementModel(security, parser),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.servers = normalizeServers(
|
||||||
|
parser.specUrl,
|
||||||
|
operationSpec.servers || operationSpec.pathServers || parser.spec.servers || [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (options.showExtensions) {
|
if (options.showExtensions) {
|
||||||
this.extensions = extractExtensions(operationSpec, options.showExtensions);
|
this.extensions = extractExtensions(operationSpec, options.showExtensions);
|
||||||
|
@ -138,6 +144,14 @@ export class OperationModel implements IMenuItem {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle expansion in middle panel (for callbacks, which are operations)
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
toggle() {
|
||||||
|
this.expanded = !this.expanded;
|
||||||
|
}
|
||||||
|
|
||||||
expand() {
|
expand() {
|
||||||
if (this.parent) {
|
if (this.parent) {
|
||||||
this.parent.expand();
|
this.parent.expand();
|
||||||
|
@ -224,4 +238,17 @@ export class OperationModel implements IMenuItem {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
get callbacks() {
|
||||||
|
return Object.keys(this.operationSpec.callbacks || []).map(callbackEventName => {
|
||||||
|
return new CallbackModel(
|
||||||
|
this.parser,
|
||||||
|
callbackEventName,
|
||||||
|
this.operationSpec.callbacks![callbackEventName],
|
||||||
|
this.pointer,
|
||||||
|
this.options,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,3 +10,4 @@ export * from './Schema';
|
||||||
export * from './Field';
|
export * from './Field';
|
||||||
export * from './ApiInfo';
|
export * from './ApiInfo';
|
||||||
export * from './SecuritySchemes';
|
export * from './SecuritySchemes';
|
||||||
|
export * from './Callback';
|
||||||
|
|
|
@ -10,9 +10,7 @@ const {
|
||||||
createGlobalStyle,
|
createGlobalStyle,
|
||||||
keyframes,
|
keyframes,
|
||||||
ThemeProvider,
|
ThemeProvider,
|
||||||
} = (styledComponents as any) as styledComponents.ThemedStyledComponentsModule<
|
} = styledComponents as styledComponents.ThemedStyledComponentsModule<ResolvedThemeInterface>;
|
||||||
ResolvedThemeInterface
|
|
||||||
>;
|
|
||||||
|
|
||||||
export const media = {
|
export const media = {
|
||||||
lessThan(breakpoint, print?: boolean) {
|
lessThan(breakpoint, print?: boolean) {
|
||||||
|
|
|
@ -37,6 +37,10 @@ const defaultTheme: ThemeInterface = {
|
||||||
dark: ({ colors }) => darken(colors.tonalOffset, colors.error.main),
|
dark: ({ colors }) => darken(colors.tonalOffset, colors.error.main),
|
||||||
contrastText: ({ colors }) => readableColor(colors.error.main),
|
contrastText: ({ colors }) => readableColor(colors.error.main),
|
||||||
},
|
},
|
||||||
|
gray: {
|
||||||
|
50: '#FAFAFA',
|
||||||
|
100: '#F5F5F5',
|
||||||
|
},
|
||||||
text: {
|
text: {
|
||||||
primary: '#333333',
|
primary: '#333333',
|
||||||
secondary: ({ colors }) => lighten(colors.tonalOffset, colors.text.primary),
|
secondary: ({ colors }) => lighten(colors.tonalOffset, colors.text.primary),
|
||||||
|
@ -229,6 +233,10 @@ export interface ResolvedThemeInterface {
|
||||||
success: ColorSetting;
|
success: ColorSetting;
|
||||||
warning: ColorSetting;
|
warning: ColorSetting;
|
||||||
error: ColorSetting;
|
error: ColorSetting;
|
||||||
|
gray: {
|
||||||
|
50: string;
|
||||||
|
100: string;
|
||||||
|
};
|
||||||
border: {
|
border: {
|
||||||
light: string;
|
light: string;
|
||||||
dark: string;
|
dark: string;
|
||||||
|
|
2
src/types/open-api.d.ts
vendored
2
src/types/open-api.d.ts
vendored
|
@ -196,7 +196,7 @@ export interface OpenAPILink {
|
||||||
export type OpenAPIHeader = Omit<OpenAPIParameter, 'in' | 'name'>;
|
export type OpenAPIHeader = Omit<OpenAPIParameter, 'in' | 'name'>;
|
||||||
|
|
||||||
export interface OpenAPICallback {
|
export interface OpenAPICallback {
|
||||||
$ref?: string;
|
[name: string]: OpenAPIPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenAPIComponents {
|
export interface OpenAPIComponents {
|
||||||
|
|
|
@ -8,9 +8,9 @@ const getInnerHtml = require('./helpers').getInnerHtml;
|
||||||
const URL = 'index.html';
|
const URL = 'index.html';
|
||||||
|
|
||||||
function waitForInit() {
|
function waitForInit() {
|
||||||
var EC = protractor.ExpectedConditions;
|
const EC = protractor.ExpectedConditions;
|
||||||
var $apiInfo = $('api-info');
|
const $apiInfo = $('api-info');
|
||||||
var $errorMessage = $('.redoc-error')
|
const $errorMessage = $('.redoc-error');
|
||||||
browser.wait(EC.or(EC.visibilityOf($apiInfo), EC.visibilityOf($errorMessage)), 60000);
|
browser.wait(EC.or(EC.visibilityOf($apiInfo), EC.visibilityOf($errorMessage)), 60000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ function basicTests(swaggerUrl, title) {
|
||||||
specUrl += `?url=${encodeURIComponent(swaggerUrl)}`;
|
specUrl += `?url=${encodeURIComponent(swaggerUrl)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach(done => {
|
||||||
browser.get(specUrl);
|
browser.get(specUrl);
|
||||||
waitForInit();
|
waitForInit();
|
||||||
fixFFTest(done);
|
fixFFTest(done);
|
||||||
|
@ -31,11 +31,11 @@ function basicTests(swaggerUrl, title) {
|
||||||
verifyNoBrowserErrors();
|
verifyNoBrowserErrors();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should init redoc without errors', (done) => {
|
it('should init redoc without errors', done => {
|
||||||
let $redoc = $('redoc');
|
const $redoc = $('redoc');
|
||||||
expect($redoc.isPresent()).toBe(true);
|
expect($redoc.isPresent()).toBe(true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let $operations = $$('operation');
|
const $operations = $$('operation');
|
||||||
expect($operations.count()).toBeGreaterThan(0);
|
expect($operations.count()).toBeGreaterThan(0);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -45,11 +45,10 @@ function basicTests(swaggerUrl, title) {
|
||||||
|
|
||||||
basicTests(null, 'Extended Petstore');
|
basicTests(null, 'Extended Petstore');
|
||||||
|
|
||||||
|
|
||||||
describe('Scroll sync', () => {
|
describe('Scroll sync', () => {
|
||||||
let specUrl = URL;
|
const specUrl = URL;
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach(done => {
|
||||||
browser.get(specUrl);
|
browser.get(specUrl);
|
||||||
waitForInit();
|
waitForInit();
|
||||||
fixFFTest(done);
|
fixFFTest(done);
|
||||||
|
@ -57,25 +56,31 @@ describe('Scroll sync', () => {
|
||||||
|
|
||||||
it('should update active menu entries on page scroll forwards', () => {
|
it('should update active menu entries on page scroll forwards', () => {
|
||||||
scrollToEl('[section="tag/store"]').then(() => {
|
scrollToEl('[section="tag/store"]').then(() => {
|
||||||
expect(getInnerHtml('.menu-item.menu-item-depth-1.active > .menu-item-header')).toContain('store');
|
expect(getInnerHtml('.menu-item.menu-item-depth-1.active > .menu-item-header')).toContain(
|
||||||
|
'store',
|
||||||
|
);
|
||||||
expect(getInnerHtml('.selected-tag')).toContain('store');
|
expect(getInnerHtml('.selected-tag')).toContain('store');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update active menu entries on page scroll backwards', () => {
|
it('should update active menu entries on page scroll backwards', () => {
|
||||||
scrollToEl('[operation-id="getPetById"]').then(() => {
|
scrollToEl('[operation-id="getPetById"]').then(() => {
|
||||||
expect(getInnerHtml('.menu-item.menu-item-depth-1.active .menu-item-header')).toContain('pet');
|
expect(getInnerHtml('.menu-item.menu-item-depth-1.active .menu-item-header')).toContain(
|
||||||
|
'pet',
|
||||||
|
);
|
||||||
expect(getInnerHtml('.selected-tag')).toContain('pet');
|
expect(getInnerHtml('.selected-tag')).toContain('pet');
|
||||||
expect(getInnerHtml('.menu-item.menu-item-depth-2.active .menu-item-header')).toContain('Find pet by ID');
|
expect(getInnerHtml('.menu-item.menu-item-depth-2.active .menu-item-header')).toContain(
|
||||||
|
'Find pet by ID',
|
||||||
|
);
|
||||||
expect(getInnerHtml('.selected-endpoint')).toContain('Find pet by ID');
|
expect(getInnerHtml('.selected-endpoint')).toContain('Find pet by ID');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Language tabs sync', () => {
|
describe('Language tabs sync', () => {
|
||||||
let specUrl = URL;
|
const specUrl = URL;
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach(done => {
|
||||||
browser.get(specUrl);
|
browser.get(specUrl);
|
||||||
waitForInit();
|
waitForInit();
|
||||||
fixFFTest(done);
|
fixFFTest(done);
|
||||||
|
@ -84,10 +89,10 @@ describe('Language tabs sync', () => {
|
||||||
// skip as it fails for no reason on IE on sauce-labs
|
// skip as it fails for no reason on IE on sauce-labs
|
||||||
// TODO: fixme
|
// TODO: fixme
|
||||||
xit('should sync language tabs', () => {
|
xit('should sync language tabs', () => {
|
||||||
var $item = $$('[operation-id="addPet"] tabs > ul > li').last();
|
const $item = $$('[operation-id="addPet"] tabs > ul > li').last();
|
||||||
// check if correct item
|
// check if correct item
|
||||||
expect($item.getText()).toContain('PHP');
|
expect($item.getText()).toContain('PHP');
|
||||||
var EC = protractor.ExpectedConditions;
|
const EC = protractor.ExpectedConditions;
|
||||||
browser.wait(EC.elementToBeClickable($item), 5000);
|
browser.wait(EC.elementToBeClickable($item), 5000);
|
||||||
$item.click().then(() => {
|
$item.click().then(() => {
|
||||||
expect($('[operation-id="updatePet"] li.active').getText()).toContain('PHP');
|
expect($('[operation-id="updatePet"] li.active').getText()).toContain('PHP');
|
||||||
|
@ -96,8 +101,7 @@ describe('Language tabs sync', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.env.JOB === 'e2e-guru') {
|
if (process.env.JOB === 'e2e-guru') {
|
||||||
describe('APIs.guru specs test', ()=> {
|
describe('APIs.guru specs test', () => {
|
||||||
|
|
||||||
// global.apisGuruList was loaded in onPrepare method of protractor config
|
// global.apisGuruList was loaded in onPrepare method of protractor config
|
||||||
let apisGuruList = global.apisGuruList;
|
let apisGuruList = global.apisGuruList;
|
||||||
|
|
||||||
|
@ -118,11 +122,11 @@ if (process.env.JOB === 'e2e-guru') {
|
||||||
console.log('Running on a short APIs guru list');
|
console.log('Running on a short APIs guru list');
|
||||||
apisGuruList = eachNth(apisGuruList, 20);
|
apisGuruList = eachNth(apisGuruList, 20);
|
||||||
} else {
|
} else {
|
||||||
console.log('Running on full APIs guru list')
|
console.log('Running on full APIs guru list');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let apiName of Object.keys(apisGuruList)) {
|
for (const apiName of Object.keys(apisGuruList)) {
|
||||||
let apiInfo = apisGuruList[apiName].versions[apisGuruList[apiName].preferred];
|
const apiInfo = apisGuruList[apiName].versions[apisGuruList[apiName].preferred];
|
||||||
let url = apiInfo.swaggerUrl;
|
let url = apiInfo.swaggerUrl;
|
||||||
|
|
||||||
// temporary hack due to this issue: https://github.com/substack/https-browserify/issues/6
|
// temporary hack due to this issue: https://github.com/substack/https-browserify/issues/6
|
||||||
|
|
Loading…
Reference in New Issue
Block a user