From c9a06dd22ad75eac80d38f144673655264197c34 Mon Sep 17 00:00:00 2001 From: AJ Rice <53190766+ajrice6713@users.noreply.github.com> Date: Thu, 19 Jan 2023 17:26:27 -0500 Subject: [PATCH 1/2] DX-3119 Findings --- DX-3119.md | 45 + demo/openapi.yaml | 1861 ++---------------------------- package-lock.json | 8 +- src/services/models/Example.ts | 11 +- src/services/models/MediaType.ts | 27 +- src/utils/openapi.ts | 4 + 6 files changed, 169 insertions(+), 1787 deletions(-) create mode 100644 DX-3119.md diff --git a/DX-3119.md b/DX-3119.md new file mode 100644 index 0000000000..767e2d7795 --- /dev/null +++ b/DX-3119.md @@ -0,0 +1,45 @@ +# Adding XML Example Support to ReDoc + +1. Create a new XML Object in `./src/services/models` with the following properties + 1. Name: string + 2. Attribute: boolean + 3. prefix: string + 4. namespace: string + 5. wrapped: true + * This is for wrapped arrays +2. Modify `./src/services/models/Field.ts` to include the above newly created field as an optional property +3. Modify `./src/utils/openapi.ts` to add a function checking whether or not a content type is XML + ```js line 167 + export function isXmlLike(contentType: string): boolean { + return contentType.search(/xml/i) !== -1; + } + // OR + export function contentTypeIsXml(contentType: string): boolean { + return contentType === 'application/xml'; + } + ``` +4. Update `./src/services/models/MediaType.ts` to: + * Import the function created in step 3 to line 7 + ```js + import { isJsonLike, contentTypeIsXml, mapValues } from '../../utils'; + ``` + * Create a new `generateXmlExample` function to generate the XML example from the OpenAPI Schema + * Add logic to the if/else to generate examples for XML Schemas + ```js Line 51 + } else if (contentTypeIsXml(name)) { + this.generateXmlExample(parser, info); + } + ``` + * A dependancy they are using, `openapi-sampler` to generate examples, does not and will not support XML samples + * https://github.com/Redocly/openapi-sampler/issues/121 +5. Update `./src/services/OpenApiParser.ts` `deref()` function to work for xml schemas +6. Update the return statement in `./src/services/models/Example.ts` to generate XML examples + ```js Line 53 + } else if (isXmlLike(mimeType)) { + try { + return JSON.parse(txt); // this needs to parse `txt` to XML + } catch (e) { + return txt; + } + } + ``` diff --git a/demo/openapi.yaml b/demo/openapi.yaml index 790a76c761..79121ba77c 100644 --- a/demo/openapi.yaml +++ b/demo/openapi.yaml @@ -1,1815 +1,116 @@ -openapi: 3.0.0 -servers: - - url: //petstore.swagger.io/v2 - description: Default server - - url: //petstore.swagger.io/sandbox - description: Sandbox server +openapi: 3.0.3 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. - - # Introduction - This API is documented in **OpenAPI format** and is based on - [Petstore 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/Redocly/redoc) documentation. In addition to standard - OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/main/docs/redoc-vendor-extensions.md). - - # OpenAPI Specification - This API is documented in **OpenAPI format** and is based on - [Petstore 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/Redocly/redoc) documentation. In addition to standard - OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/main/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. - - # Authentication - - Petstore offers two forms of authentication: - - API Key - - OAuth2 - OAuth2 - an open protocol to allow secure authorization in a simple - and standard method from web, mobile and desktop applications. - - - + title: Test XML API version: 1.1.0 - title: Swagger Petstore - termsOfService: 'http://swagger.io/terms/' contact: - name: API Support - email: apiteam@swagger.io - url: https://github.com/Redocly/redoc - x-logo: - url: 'https://redocly.github.io/redoc/petstore-logo.png' - altText: Petstore logo - license: - name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' -externalDocs: - description: Find out how to create Github repo for your OpenAPI spec. - url: 'https://github.com/Rebilly/generator-openapi-repo' -tags: - - name: pet - description: Everything about your Pets - - name: Tn Option - description: Everything about your TN Options - - name: store - description: Access to Petstore orders - - name: user - description: Operations about user - - name: pet_model - x-displayName: The Pet Model - description: | - - - name: store_model - x-displayName: The Order Model - description: | - -x-tagGroups: - - name: General - tags: - - pet - - store - - Tn Option - - name: User Management - tags: - - user - - name: Models - tags: - - pet_model - - store_model -security: - - {} + name: Bandwidth Support + email: support@bandwidth.com + url: https://support.bandwidth.com + description: >- + A Test XML API +servers: + - url: https://xml.bandwidth.com/api/v1 + description: Production paths: - /pet: - parameters: - - name: Accept-Language - in: header - description: 'The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US' - example: en-US - required: false - schema: - type: string - default: en-AU - - name: cookieParam - in: cookie - description: Some cookie - required: true - schema: - type: integer - format: int64 + /testXml: post: + summary: Test XML + description: A test XML API endpoint. + operationId: testXml tags: - - pet - summary: Add a new pet to the store - description: Add new pet to the store inventory. - operationId: addPet - responses: - '405': - description: Invalid input - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - x-codeSamples: - - 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()); - } - requestBody: - $ref: '#/components/requestBodies/Pet' - put: - tags: - - pet - summary: Update an existing pet - description: '' - operationId: updatePet - responses: - '400': - description: Invalid ID supplied - '404': - description: Pet not found - '405': - description: Validation exception - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - x-codeSamples: - - lang: PHP - source: | - $form = new \PetStore\Entities\Pet(); - $form->setPetId(1); - $form->setPetType("Dog"); - $form->setName("Rex"); - // set other fields - try { - $pet = $client->pets()->update($form); - } catch (UnprocessableEntityException $e) { - var_dump($e->getErrors()); - } + - Test XML requestBody: - $ref: '#/components/requestBodies/Pet' - '/accounts/{accountId}/tnoptions/{orderid}/history': - get: - description: Retrieve the history information associated with an order. - operationId: GetOrderHistory - parameters: - - description: '' - in: path - name: orderid - required: true - schema: - type: string + $ref: '#/components/requestBodies/sampleXmlRequest' responses: '200': - content: - application/xml: - examples: - application/xml: - value: |- - - - - 2020-09-17T08:56:39.607Z - Order has been received by the system. - admin - received - - - 2020-09-17T08:56:39.743Z - Order processing has started. - admin - processing - - - 2020-09-17T08:56:39.820Z - Order is complete. - admin - complete - - - description: >- - The history payload is a set of history records, each of which - contains: - - - -
OrderDateThe date that the order history event happened
StatusThe new state of the TN Option order - RECEIVED, PROCESSING, COMPLETE, PARTIAL, FAILED
NoteA Note or additional information included with the state change
AuthorThe user id that implemented the state change

- '404': - content: - application/xml: {} - description: Not found. The order id does not exist in the system. - summary: Retrieve TN Option order history - tags: - - Tn Option - '/accounts/{accountId}/tnoptions': - get: - description: >- - Retrieve a list of the TN Option orders that are associated with the - account. A maximum of 1,000 orders can be retrieved per request. If no - date range or specific query parameter (marked by * below) is - provided, the order results will be limited to the last two years. - operationId: GetTnOptionOrders - parameters: - - description: The status of the TN Option order being searched for. - example: PROCESSING - in: query - name: status - required: false - schema: - items: - type: string - type: array - - description: * A Telephone Number (TN) that is referenced in the order. - example: '9199918388' - in: query - name: tn - required: false - schema: - type: string - - description: >- - * The Customer Order ID is an ID assigned by the account - owner to provide a reference number for the TN Option order. - example: ABCCorp12345 - in: query - name: customerOrderId - required: false - schema: - type: string - - description: >- - For Date-based searches, the starting date of a date range - (inclusive) that will be used to find TN Option Orders that were - modified within the date range. It is in the form yyyy-MM-dd. - example: '2013-10-22' - in: query - name: modifiedDateFrom - required: false - schema: - type: string - - description: >- - For Date-based searches, the ending date of a date range (inclusive) - that will be used to find TN Option Orders that were modified within - the date range. It is in the form yyyy-MM-dd. - example: '2013-10-25' - in: query - name: modifiedDateTo - required: false - schema: - type: string - - description: >- - Checks the order's creation date against this value. Orders that - have a creation date after this date will be included. Format is - yyyy-MM-dd. - example: '2013-10-22' - in: query - name: createdDateFrom - required: false - schema: - type: string - - description: >- - Checks the order's creation date against this value. Orders that - have a creation date before this date will be included. Format is - yyyy-MM-dd. - example: '2013-10-25' - in: query - name: createdDateTo - required: false - schema: - type: string - - description: >- - Checks the order's last modified date against this value. Orders - that have a modification date after this date will be included. - Format is yyyy-MM-dd. - example: '2013-10-25' - in: query - name: lastModifiedAfter - required: false - schema: - type: string - - description: >- - This is the user-name of the user that last modified the TN Option - Order. - example: smckinnon - in: query - name: lastModifiedBy - required: false - schema: - type: string - - description: >- - * This search parameter represents the internal Bandwidth - Dashboard API ID that has been assigned to the TN Option Order. This - parameter is the first few characters of the internal ID - the - entire ID does not need to be specified. - example: ed1c0bed-e2 - in: query - name: orderIdFragment - required: false - schema: - type: string - - description: >- - If set to true, a list of order details will be displayed instead - the summary information. - in: query - name: orderDetails - required: false - schema: - type: boolean - responses: - '200': - content: - application/xml: - examples: - example: - value: |- - - - - 2 - - 14 - 2 - jbm - 2016-01-15T12:01:14.363Z - 2016-01-15T12:01:14.324Z - tn_option - FAILED - ddbdc72e-dc27-490c-904e-d0c11291b095 - - - 14 - 3 - jbm - 2016-01-15T11:22:58.969Z - 2016-01-15T11:22:58.789Z - tn_option - COMPLETE - 409033ee-88ec-43e3-85f3-538f30733963 - - - - - 2 - - 2016-01-15T12:01:14.324Z - 14 - jbm - ddbdc72e-dc27-490c-904e-d0c11291b095 - 2016-01-15T12:01:14.363Z - FAILED - - - 10digit - 10digit - testUser1 - 6042661720 - on - true - on - sip:+12345678901@1.2.3.4:5060 - - 2018551020 - - - - off - false - off - - 2018551025 - - - - - - 5076 - Telephone number is not available. - 2018551025 - - - 5076 - Telephone number is not available. - 2018551020 - - - - - 2016-01-15T11:22:58.789Z - 14 - jbm - 409033ee-88ec-43e3-85f3-538f30733963 - 2016-01-15T11:22:58.969Z - COMPLETE - - - on - - 2174101601 - - - - off - - 2174101602 - - - - systemdefault - - 2174101603 - - - - - - - - description: >- - The descriptive payload for the TN Option Orders query provides a - broad range of information about the TN Option Orders found by the - query, including the data associated with the order, the state of - the order, and details about the order if it was successful. - '404': - content: - application/xml: {} - description: > - Not Found. If any errors are found in the processing of the query - string a 404 will be returned. Note that parameters that are not - recognized are not considered errors, and are just ignored. - summary: Retrieve list TN Option orders - tags: - - Tn Option + $ref: '#/components/responses/sampleXmlResponse' + /testJson: post: - description: >- - Create TN Option order to assign line features to the telephone - number.

Attribute description: - operationId: CreateTnOptionOrder - requestBody: - content: - application/xml: - examples: - example: - value: |- - - - TnOptionOrder1 - - - 10digit - 10digit - testUser1 - 6042661720 - on - a1b2c3 - true - on - sip:+12345678901@1.2.3.4:5060 - - M - campaignId010 - asSpecified - - - 2018551020 - - - - off - false - off - - - 6105552502 - 1 - 100 - - - sip:clarkkent@dailyplanet.com - 2 - 100 - - - 192.168.20.21 - 3 - 50 - - - 7075552509 - 3 - 50 - - - myhost.bandwidth.example - 4 - 100 - - asSpecified - - - 2018551025 - - - - true - - 2018551026 - - - - - responses: - '201': - content: - application/xml: - examples: - example: - value: |- - - - - 2016-01-15T12:01:14.324Z - 14 - jbm - ddbdc72e-dc27-490c-904e-d0c11291b095 - 2016-01-15T12:01:14.324Z - RECEIVED - - - 10digit - 10digit - testUser1 - 6042661720 - on - true - on - - 2018551020 - - - - off - false - off - - 2018551025 - - - - - - - description: Created - '400': - content: - application/xml: - examples: - example: - value: |- - - - - 5081 - Number Format 'wrong' is invalid. - - - description: >- - Bad Request A 400 response Indicates that the order could not be - created. Error text and an error code will be provided in the - ErrorList element. - '409': - content: - application/xml: - examples: - example: - value: |- - - - - 5200 - Origination route plan is not enabled for the account. - - - description: >- - Conflict. Error text and an error code will be provided in the - ErrorList element. - summary: Create TN Option order - tags: - - Tn Option - '/accounts/{accountId}/tnoptions/{orderid}': - get: - description: Retrieve information about a TN Option Order with specified ID. - operationId: GetTnOptionOrder - parameters: - - description: '' - in: path - name: orderid - required: true - schema: - type: string - responses: - '200': - content: - application/xml: - examples: - example: - value: |- - - - 2016-01-15T11:22:58.789Z - 14 - jbm - 409033ee-88ec-43e3-85f3-538f30733963 - 2016-01-15T11:22:58.969Z - COMPLETE - - - on - on - - 2174101601 - - - - off - - 2174101602 - - - - systemdefault - sip:+12345678901@1.2.3.4:5060 - - 2174101603 - - - - - - - 2174101601 - SMS is already Enabled or number is in processing. - - - - description: >- - The descriptive payload for the TN Option Orders query provides a - broad range of information about the TN Option Order identified in - the URL. Included amongst the information is: - '404': - content: - application/xml: - examples: - example: - value: |- - - - - The resource does not exist - - - description: Not found. The order id does not exist in the system. - summary: Retrieve TN Option order + summary: Test Json + description: A test JSON API endpoint. + operationId: testJson tags: - - Tn Option - '/pet/{petId}': - get: - tags: - - pet - summary: Find pet by ID - description: Returns a single pet - operationId: getPetById - parameters: - - name: petId - in: path - description: ID of pet to return - required: true - deprecated: true - schema: - type: integer - format: int64 - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - application/xml: - schema: - $ref: '#/components/schemas/Pet' - '400': - description: Invalid ID supplied - '404': - description: Pet not found - security: - - api_key: [] - post: - tags: - - pet - summary: Updates a pet in the store with form data - description: '' - operationId: updatePetWithForm - parameters: - - name: petId - in: path - description: ID of pet that needs to be updated - required: true - schema: - type: integer - format: int64 - responses: - '405': - description: Invalid input - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - requestBody: - content: - application/x-www-form-urlencoded: - schema: - type: object - properties: - name: - description: Updated name of the pet - type: string - status: - description: Updated status of the pet - type: string - delete: - tags: - - pet - summary: Deletes a pet - description: '' - operationId: deletePet - parameters: - - name: api_key - in: header - required: false - schema: - type: string - example: 'Bearer ' - - name: petId - in: path - description: Pet id to delete - required: true - schema: - type: integer - format: int64 - responses: - '400': - description: Invalid pet value - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - '/pet/{petId}/uploadImage': - post: - tags: - - pet - summary: uploads an image - description: '' - operationId: uploadFile - parameters: - - name: petId - in: path - description: ID of pet to update - required: true - schema: - type: integer - format: int64 - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ApiResponse' - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - requestBody: - content: - application/octet-stream: - schema: - type: string - format: binary - /pet/findByStatus: - get: - tags: - - pet - summary: Finds Pets by status - description: Multiple status values can be provided with comma separated strings - operationId: findPetsByStatus - parameters: - - name: status - in: query - description: Status values that need to be considered for filter - required: true - style: form - schema: - type: array - minItems: 1 - maxItems: 3 - items: - type: string - enum: - - available - - pending - - sold - default: available - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Pet' - application/xml: - schema: - type: array - items: - $ref: '#/components/schemas/Pet' - '400': - description: Invalid status value - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - /pet/findByTags: - get: - tags: - - pet - summary: Finds Pets by tags - description: >- - Multiple tags can be provided with comma separated strings. Use tag1, - tag2, tag3 for testing. - operationId: findPetsByTags - deprecated: true - parameters: - - name: tags - in: query - description: Tags to filter by - required: true - style: form - schema: - type: array - items: - type: string - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Pet' - application/xml: - schema: - type: array - maxItems: 999 - items: - maxItems: 111 - $ref: '#/components/schemas/Pet' - '400': - description: Invalid tag value - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - /store/inventory: - get: - tags: - - store - summary: Returns pet inventories by status - description: Returns a map of status codes to quantities - operationId: getInventory - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: object - minProperties: 2 - additionalProperties: - type: integer - format: int32 - security: - - api_key: [] - /store/order: - post: - tags: - - store - summary: Place an order for a pet - description: '' - operationId: placeOrder - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/Order' - application/xml: - schema: - $ref: '#/components/schemas/Order' - '400': - description: Invalid Order - content: - application/json: - example: - status: 400 - message: 'Invalid Order' + - Test Json requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Order' - description: order placed for purchasing the pet - required: true - '/store/order/{orderId}': - get: - tags: - - store - 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 - parameters: - - name: orderId - in: path - description: ID of pet that needs to be fetched - required: true - schema: - type: integer - format: int64 - minimum: 1 - maximum: 5 + $ref: '#/components/requestBodies/sampleJsonRequest' responses: '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/Order' - application/xml: - schema: - $ref: '#/components/schemas/Order' - '400': - description: Invalid ID supplied - '404': - description: Order not found - delete: - tags: - - store - summary: Delete purchase order by ID - description: >- - For valid response try integer IDs with value < 1000. Anything above - 1000 or nonintegers will generate API errors - operationId: deleteOrder - parameters: - - name: orderId - in: path - description: ID of the order that needs to be deleted - required: true - schema: - type: string - minimum: 1 - responses: - '400': - description: Invalid ID supplied - '404': - 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: | - - - 123 - inProgress - 2018-10-19T16:46:45Z - - 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-codeSamples: - - 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: | - - - 123 - inProgress - 2018-10-19T16:46:45Z - - 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: - post: - tags: - - user - summary: Create user - description: This can only be done by the logged in user. - operationId: createUser - responses: - default: - description: successful operation - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/User' - description: Created user object - required: true - '/user/{username}': - get: - tags: - - user - summary: Get user by user name - description: '' - operationId: getUserByName - parameters: - - name: username - in: path - description: 'The name that needs to be fetched. Use user1 for testing. ' - required: true - schema: - type: string - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/User' - application/xml: - schema: - $ref: '#/components/schemas/User' - '400': - description: Invalid username supplied - '404': - description: User not found - put: - tags: - - user - summary: Updated user - description: This can only be done by the logged in user. - operationId: updateUser - parameters: - - name: username - in: path - description: name that need to be deleted - required: true - schema: - type: string - responses: - '400': - description: Invalid user supplied - '404': - description: User not found - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/User' - description: Updated user object - required: true - delete: - tags: - - user - summary: Delete user - description: This can only be done by the logged in user. - operationId: deleteUser - parameters: - - name: username - in: path - description: The name that needs to be deleted - required: true - schema: - type: string - responses: - '400': - description: Invalid username supplied - '404': - description: User not found - /user/createWithArray: - post: - tags: - - user - summary: Creates list of users with given input array - description: '' - operationId: createUsersWithArrayInput - responses: - default: - description: successful operation - requestBody: - $ref: '#/components/requestBodies/UserArray' - /user/createWithList: - post: - tags: - - user - summary: Creates list of users with given input array - description: '' - operationId: createUsersWithListInput - responses: - default: - description: successful operation - requestBody: - $ref: '#/components/requestBodies/UserArray' - /user/login: - get: - tags: - - user - summary: Logs user into the system - description: '' - operationId: loginUser - parameters: - - name: username - in: query - description: The user name for login - required: true - schema: - type: string - - name: password - in: query - description: The password for login in clear text - required: true - schema: - type: string - responses: - '200': - description: successful operation - headers: - X-Rate-Limit: - description: calls per hour allowed by the user - schema: - type: integer - format: int32 - X-Expires-After: - description: date in UTC when token expires - schema: - type: string - format: date-time - content: - application/json: - schema: - type: string - examples: - response: - value: OK - application/xml: - schema: - type: string - examples: - response: - value: OK - text/plain: - examples: - response: - value: OK - '400': - description: Invalid username/password supplied - /user/logout: - get: - tags: - - user - summary: Logs out current logged in user session - description: '' - operationId: logoutUser - responses: - default: - description: successful operation + $ref: '#/components/responses/sampleJsonResponse' components: schemas: - ApiResponse: - type: object - properties: - code: - type: integer - format: int32 - type: - type: string - message: - type: string - Cat: - description: A representation of a cat - allOf: - - $ref: '#/components/schemas/Pet' - - type: object - properties: - huntingSkill: - type: string - description: The measured skill for hunting - default: lazy - example: adventurous - enum: - - clueless - - lazy - - adventurous - - aggressive - required: - - huntingSkill - Category: + sampleObject: type: object properties: id: - description: Category ID - allOf: - - $ref: '#/components/schemas/Id' + type: integer + example: 1 + xml: + name: Id name: - description: Category name type: string - minLength: 1 - sub: - description: Test Sub Category + example: TestName + xml: + attribute: true + date: + type: string + format: date + example: 2023-01-03 + xml: + name: Date + nestedObject: type: object properties: - prop1: + createdDate: type: string - description: Dumb Property - xml: - name: Category - Dog: - description: A representation of a dog - allOf: - - $ref: '#/components/schemas/Pet' - - type: object - properties: - packSize: - type: integer - format: int32 - 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: '#/components/schemas/Pet' - - type: object - properties: - honeyPerDay: - type: number - description: Average amount of honey produced per day in ounces - example: 3.14 - multipleOf: .01 - required: - - honeyPerDay - Id: - type: integer - format: int64 - readOnly: true - Order: - type: object - properties: - id: - description: Order ID - allOf: - - $ref: '#/components/schemas/Id' - petId: - description: Pet ID - allOf: - - $ref: '#/components/schemas/Id' - quantity: - type: integer - format: int32 - minimum: 1 - default: 1 - shipDate: - description: Estimated ship date - type: string - format: date-time - status: - type: string - description: Order Status - enum: - - placed - - approved - - delivered - complete: - description: Indicates whenever order was completed or not - type: boolean - default: false - readOnly: true - requestId: - description: Unique Request Id - type: string - writeOnly: true - xml: - name: Order - Pet: - type: object - required: - - name - - photoUrls - discriminator: - propertyName: petType - mapping: - cat: '#/components/schemas/Cat' - dog: '#/components/schemas/Dog' - bee: '#/components/schemas/HoneyBee' - properties: - id: - externalDocs: - description: 'Find more info here' - url: 'https://example.com' - description: Pet ID - allOf: - - $ref: '#/components/schemas/Id' - category: - description: Categories this pet belongs to - allOf: - - $ref: '#/components/schemas/Category' - name: - description: The name given to a pet - type: string - example: Guru - photoUrls: - description: The list of URL to a cute photos featuring pet - type: array - maxItems: 20 - xml: - name: photoUrl - wrapped: true - items: - type: string - format: url - friend: - allOf: - - $ref: '#/components/schemas/Pet' - tags: - description: Tags attached to the pet - type: array - minItems: 1 + format: date + example: 2023-01-03 + xml: + name: createdDate + subId: + type: string + example: abc123 + xml: + attribute: True xml: - name: tag - wrapped: true - items: - $ref: '#/components/schemas/Tag' - status: - type: string - description: Pet status in the store - enum: - - available - - pending - - sold - petType: - description: Type of a pet - type: string - xml: - name: Pet - Tag: - type: object - properties: - id: - description: Tag ID - allOf: - - $ref: '#/components/schemas/Id' - name: - description: Tag name - type: string - minLength: 1 - xml: - name: Tag - User: - type: object - properties: - id: - $ref: '#/components/schemas/Id' - pet: - oneOf: - - $ref: '#/components/schemas/Pet' - - $ref: '#/components/schemas/Tag' - 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 - addresses: - type: array - minItems: 0 - maxLength: 10 - items: - - type: object - properties: - city: - type: string - minLength: 0 - country: - type: string - minLength: 0 - street: - description: includes build/apartment number - type: string - minLength: 0 - - type: number - additionalItems: - type: string + name: NestedObject xml: - name: User + name: XmlObject requestBodies: - Pet: + sampleXmlRequest: + content: + application/xml: + schema: + $ref: '#/components/schemas/sampleObject' + sampleJsonRequest: content: application/json: schema: - allOf: - - description: My Pet - title: Pettie - - $ref: '#/components/schemas/Pet' + $ref: '#/components/schemas/sampleObject' + responses: + sampleXmlResponse: + description: A sample XML response. + content: application/xml: schema: - type: 'object' - properties: - name: - type: string - description: hooray - description: Pet object that needs to be added to the store - required: true - UserArray: + $ref: '#/components/schemas/sampleObject' + sampleJsonResponse: + description: A sample Json response. content: - application/json: + application/xml: schema: - type: array - items: - $ref: '#/components/schemas/User' - description: List of user object - required: true + $ref: '#/components/schemas/sampleObject' securitySchemes: - petstore_auth: - description: | - Get access to data while protecting your account credentials. - OAuth2 is also a safer and more secure way to give you access. - type: oauth2 - flows: - implicit: - authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog' - scopes: - 'write:pets': modify pets in your account - 'read:pets': read your pets - api_key: - description: > - For this sample, you can use the api key `special-key` to test the - authorization filters. - type: apiKey - name: api_key - in: header - examples: - Order: - value: - quantity: 1 - shipDate: '2018-10-19T16:46:45Z' - status: placed - complete: false -x-webhooks: - newPet: - post: - summary: New pet - description: Information about a new pet in the systems - operationId: newPet - tags: - - pet - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - responses: - '200': - description: Return a 200 status to indicate that the data was received successfully + Basic: + type: http + scheme: basic + description: >- + Basic authentication is a simple authentication scheme built into the + HTTP protocol. To use it, send your HTTP requests with an Authorization + header that contains the word Basic followed by a space and a + base64-encoded string `username:password`. + + Example: `Authorization: Basic ZGVtbZpwQDU1dzByZA==` +security: + - Basic: [] +tags: + - name: Test XML diff --git a/package-lock.json b/package-lock.json index 3c50664733..2a6f4f1613 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "redoc", - "version": "2.0.0", + "name": "bandwidth-redoc", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "redoc", - "version": "2.0.0", + "name": "bandwidth-redoc", + "version": "0.1.0", "license": "MIT", "dependencies": { "@redocly/openapi-core": "^1.0.0-beta.104", diff --git a/src/services/models/Example.ts b/src/services/models/Example.ts index b5f7e0ff97..93896a90f3 100644 --- a/src/services/models/Example.ts +++ b/src/services/models/Example.ts @@ -1,5 +1,5 @@ import type { OpenAPIEncoding, OpenAPIExample, Referenced } from '../../types'; -import { isFormUrlEncoded, isJsonLike, urlFormEncodePayload } from '../../utils/openapi'; +import { isFormUrlEncoded, isJsonLike, isXmlLike, urlFormEncodePayload } from '../../utils/openapi'; import type { OpenAPIParser } from '../OpenAPIParser'; const externalExamplesCache: { [url: string]: Promise } = {}; @@ -17,6 +17,8 @@ export class ExampleModel { encoding?: { [field: string]: OpenAPIEncoding }, ) { const { resolved: example } = parser.deref(infoOrRef); + console.log('Value:', example.value); + console.log('InfoOrRef:', infoOrRef); this.value = example.value; this.summary = example.summary; this.description = example.description; @@ -50,12 +52,17 @@ export class ExampleModel { } catch (e) { return txt; } + } else if (isXmlLike(mimeType)) { + try { + return JSON.parse(txt); // Needs to be changed to stringify xml + } catch (e) { + return txt; + } } else { return txt; } }); }); - return externalExamplesCache[this.externalValueUrl]; } } diff --git a/src/services/models/MediaType.ts b/src/services/models/MediaType.ts index 1b7263ae95..4573cc3865 100644 --- a/src/services/models/MediaType.ts +++ b/src/services/models/MediaType.ts @@ -4,7 +4,7 @@ import type { OpenAPIMediaType } from '../../types'; import type { RedocNormalizedOptions } from '../RedocNormalizedOptions'; import { SchemaModel } from './Schema'; -import { isJsonLike, mapValues } from '../../utils'; +import { isJsonLike, isXmlLike, mapValues } from '../../utils'; import type { OpenAPIParser } from '../OpenAPIParser'; import { ExampleModel } from './Example'; @@ -26,6 +26,7 @@ export class MediaTypeModel { info: OpenAPIMediaType, options: RedocNormalizedOptions, ) { + console.log('Name:', name); this.name = name; this.isRequestType = isRequestType; this.schema = info.schema && new SchemaModel(parser, info.schema, '', options); @@ -47,6 +48,30 @@ export class MediaTypeModel { }; } else if (isJsonLike(name)) { this.generateExample(parser, info); + } else if (isXmlLike(name)) { + this.generateXmlExample(parser, info); + } + } + + generateXmlExample(parser: OpenAPIParser, info: OpenAPIMediaType) { + const samplerOptions = { + skipReadOnly: this.isRequestType, + skipWriteOnly: !this.isRequestType, + skipNonRequired: this.isRequestType && this.onlyRequiredInSamples, + maxSampleDepth: this.generatedPayloadSamplesMaxDepth, + }; + if (this.schema) { + console.log('Info Schema:', Sampler.sample(info.schema as any, samplerOptions, parser.spec)); + this.examples = { + default: new ExampleModel( + parser, + { + value: Sampler.sample(info.schema as any, samplerOptions, parser.spec), // This creates a JSON object from schema + }, + this.name, + info.encoding, + ), + }; } } diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index 22424e90e7..2c6d9577fb 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -164,6 +164,10 @@ export function isJsonLike(contentType: string): boolean { return contentType.search(/json/i) !== -1; } +export function isXmlLike(contentType: string): boolean { + return contentType.search(/xml/i) !== -1; +} + export function isFormUrlEncoded(contentType: string): boolean { return contentType === 'application/x-www-form-urlencoded'; } From 85d92975bd51a33eb02b5ea461ce9bde5a580688 Mon Sep 17 00:00:00 2001 From: AJ Rice <53190766+ajrice6713@users.noreply.github.com> Date: Fri, 20 Jan 2023 09:13:31 -0500 Subject: [PATCH 2/2] Update notes --- DX-3119.md | 5 +++-- src/services/models/MediaType.ts | 8 +++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/DX-3119.md b/DX-3119.md index 767e2d7795..f911d6e45d 100644 --- a/DX-3119.md +++ b/DX-3119.md @@ -24,6 +24,7 @@ import { isJsonLike, contentTypeIsXml, mapValues } from '../../utils'; ``` * Create a new `generateXmlExample` function to generate the XML example from the OpenAPI Schema + * https://github.com/oozcitak/xmlbuilder2 * Add logic to the if/else to generate examples for XML Schemas ```js Line 51 } else if (contentTypeIsXml(name)) { @@ -32,8 +33,8 @@ ``` * A dependancy they are using, `openapi-sampler` to generate examples, does not and will not support XML samples * https://github.com/Redocly/openapi-sampler/issues/121 -5. Update `./src/services/OpenApiParser.ts` `deref()` function to work for xml schemas -6. Update the return statement in `./src/services/models/Example.ts` to generate XML examples +5. Update `./src/services/OpenApiParser.ts` `deref()` function to work for xml schemas? +6. Update the return statement in `./src/services/models/Example.ts` to generate XML examples? ```js Line 53 } else if (isXmlLike(mimeType)) { try { diff --git a/src/services/models/MediaType.ts b/src/services/models/MediaType.ts index 4573cc3865..bfbae0d002 100644 --- a/src/services/models/MediaType.ts +++ b/src/services/models/MediaType.ts @@ -53,6 +53,13 @@ export class MediaTypeModel { } } + /** + * This is where we will need to do some work - need to convert an object into something that the xmlbuilder2 library can understand, and then convert to XML + * + * https://stackoverflow.com/questions/24309184/whats-the-best-json-or-js-object-to-xml-converter-module-for-node-js + * @param parser + * @param info + */ generateXmlExample(parser: OpenAPIParser, info: OpenAPIMediaType) { const samplerOptions = { skipReadOnly: this.isRequestType, @@ -61,7 +68,6 @@ export class MediaTypeModel { maxSampleDepth: this.generatedPayloadSamplesMaxDepth, }; if (this.schema) { - console.log('Info Schema:', Sampler.sample(info.schema as any, samplerOptions, parser.spec)); this.examples = { default: new ExampleModel( parser,