diff --git a/.github/workflows/snapshot-release.yaml b/.github/workflows/snapshot-release.yaml index e1a4247409..488d0e656e 100644 --- a/.github/workflows/snapshot-release.yaml +++ b/.github/workflows/snapshot-release.yaml @@ -31,9 +31,14 @@ jobs: # Update Core package version jq ".version = \"$VERSION\"" packages/core/package.json > tmp.json && mv tmp.json packages/core/package.json - # Update CLI package version and the Core dependency + # Update Respect Core package version and dependencies + jq ".version = \"$VERSION\"" packages/respect-core/package.json > tmp.json && mv tmp.json packages/respect-core/package.json + jq ".dependencies[\"@redocly/openapi-core\"] = \"$VERSION\"" packages/respect-core/package.json > tmp.json && mv tmp.json packages/respect-core/package.json + + # Update CLI package version and dependencies jq ".version = \"$VERSION\"" packages/cli/package.json > tmp.json && mv tmp.json packages/cli/package.json jq ".dependencies[\"@redocly/openapi-core\"] = \"$VERSION\"" packages/cli/package.json > tmp.json && mv tmp.json packages/cli/package.json + jq ".dependencies[\"@redocly/respect-core\"] = \"$VERSION\"" packages/cli/package.json > tmp.json && mv tmp.json packages/cli/package.json # Add comment with installation instructions COMMENT="📦 A new experimental version **v$VERSION** of Redocly CLI has been published for testing. @@ -66,5 +71,9 @@ jobs: cd packages/core npm publish --tag snapshot + + cd ../respect-core + npm publish --tag snapshot + cd ../cli npm publish --tag snapshot diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d111c66fc9..3f67c7d6c3 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -28,7 +28,7 @@ jobs: run: npm run e2e env: CI: true - + SANDBOX_REBILLY_TOKEN: ${{ secrets.SANDBOX_REBILLY_TOKEN }} cli-package-test: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index bdee3c94d4..66124a916c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ output/ *.tgz redoc-static.html packages/cli/README.md +.env diff --git a/.prettierignore b/.prettierignore index cf5a3be67d..9d62bc3157 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,6 +4,7 @@ lib/ output/ *snapshot.js packages/core/src/rules/__tests__/fixtures/invalid-yaml.yaml +packages/respect-core/src/modules/runtime-expressions/abnf-parser.js __tests__/webpack-bundle/bundle-workflows/metafile.json benchmark/api-definitions/ LICENSE.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cbb6743627..3feac33810 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,11 +70,9 @@ To test local changes as a package, you can use the following steps: 1. Optionally, bump the version of the packages ([see details](#version-updating)). -1. Run `npm run pack:prepare` in the repository's root. This generates **redocly-cli.tgz** and **openapi-core.tgz** files and makes some changes to **packages/cli/package.json** file. +1. Run `npm run pack:prepare` in the repository's root. This generates **redocly-cli.tgz**, **respect-core.tgz**, and **openapi-core.tgz** files. -1. Copy **redocly-cli.tgz** file to a destination folder and then run `npm install redocly-cli.tgz` there to install Redocly CLI. To install `openapi-core` do the same but with **openapi-core.tgz** file. - -Don't forget to revert the changes to **package.json** files later. +1. Copy those **.tgz** files to a destination folder and then run `npm install redocly-cli.tgz` there to install Redocly CLI. To install `openapi-core` do the same but with **openapi-core.tgz** file. ## Contribute documentation diff --git a/__tests__/helpers.ts b/__tests__/helpers.ts index 0de06c5cda..7fbfa5f7be 100644 --- a/__tests__/helpers.ts +++ b/__tests__/helpers.ts @@ -16,7 +16,8 @@ type CLICommands = | 'push' | 'split' | 'stats' - | 'build-docs'; + | 'build-docs' + | 'respect'; export function getParams( indexEntryPoint: string, diff --git a/__tests__/respect/case-insensitive-headers/__snapshots__/case-insensitive-headers.test.ts.snap b/__tests__/respect/case-insensitive-headers/__snapshots__/case-insensitive-headers.test.ts.snap new file mode 100644 index 0000000000..4c12a4b969 --- /dev/null +++ b/__tests__/respect/case-insensitive-headers/__snapshots__/case-insensitive-headers.test.ts.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should send in request and proceed case-insensitive headers in runtime expressions 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow case-insensitive-headers.yaml / get-museum-hours + + ✓ GET /museum-hours - step get-museum-hours +    ✓ success criteria check +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +  Summary for case-insensitive-headers.yaml +   +  Workflows: 1 passed, 1 total +  Steps: 1 passed, 1 total +  Checks: 6 passed, 6 total +  Time: ms + + +┌───────────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├───────────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ ✓ case-insensitive-headers.yaml │ 1 │ 1 │ - │ - │ - │ +└───────────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + +" +`; diff --git a/__tests__/respect/case-insensitive-headers/case-insensitive-headers.test.ts b/__tests__/respect/case-insensitive-headers/case-insensitive-headers.test.ts new file mode 100644 index 0000000000..268e6c9aff --- /dev/null +++ b/__tests__/respect/case-insensitive-headers/case-insensitive-headers.test.ts @@ -0,0 +1,11 @@ +import { join } from 'path'; +import { getCommandOutput, getParams } from '../utils'; + +test('should send in request and proceed case-insensitive headers in runtime expressions', () => { + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'case-insensitive-headers.yaml'); + const args = getParams(indexEntryPoint, ['respect', fixturesPath]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); +}); diff --git a/__tests__/respect/case-insensitive-headers/case-insensitive-headers.yaml b/__tests__/respect/case-insensitive-headers/case-insensitive-headers.yaml new file mode 100644 index 0000000000..42997803fa --- /dev/null +++ b/__tests__/respect/case-insensitive-headers/case-insensitive-headers.yaml @@ -0,0 +1,42 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API + description: >- + Testing case insensitive headers in workflows + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-hours + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + parameters: + - in: header + name: CammelCasedHeader + value: custom-header + - in: header + name: CoNtenT-Type + value: first + - in: header + name: ContenT-Type + value: second + - in: header + name: ContenT-TypE + value: third + operationId: museum-api.getMuseumHours + successCriteria: + - condition: $request.header.content-type == 'third' + - condition: $request.header.CammelCasedHeader == 'custom-header' && $response.header.X-Frame-Options == 'deny' + - condition: $response.header.X-Content-Type-Options == 'nosniff' diff --git a/__tests__/respect/case-insensitive-headers/museum-api.yaml b/__tests__/respect/case-insensitive-headers/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/case-insensitive-headers/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/cat-fact-api/__snapshots__/cat-fact-api.test.ts.snap b/__tests__/respect/cat-fact-api/__snapshots__/cat-fact-api.test.ts.snap new file mode 100644 index 0000000000..cb62e52497 --- /dev/null +++ b/__tests__/respect/cat-fact-api/__snapshots__/cat-fact-api.test.ts.snap @@ -0,0 +1,204 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`cats api test case 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow auto-cat.yaml / get-breeds-workflow + + ✗ GET /breeds - step get-breeds-step +    ✓ status code check (Response code 200 matches one of description codes: [200]) +    ✓ content-type check +    ✗ schema check + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow auto-cat.yaml / get-fact-workflow + + ✓ GET /fact - step get-fact-step +    ✓ status code check (Response code 200 matches one of description codes: [200, 404]) +    ✓ content-type check +    ✓ schema check + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow auto-cat.yaml / get-facts-workflow + + ✗ GET /facts - step get-facts-step +    ✓ status code check (Response code 200 matches one of description codes: [200]) +    ✓ content-type check +    ✗ schema check + + +  Failed tests info: + +  Workflow name: get-breeds-workflow + +    stepId - get-breeds-step +    ✗ schema check +       +      TYPE must be array +       +      > 1 | { +      | ^ +      > 2 | "current_page": 1, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 3 | "data": [ +      | ^^^^^^^^^^^^^^^^^^^^ +      > 4 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 5 | "breed": "Abyssinian", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 6 | "country": "Ethiopia", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 7 | "origin": "Natural/Standard", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 8 | "coat": "Short", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 9 | "pattern": "Ticked" +      | ^^^^^^^^^^^^^^^^^^^^ +      > 10 | }, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 11 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 12 | "breed": "Aegean", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 13 | "country": "Greece", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 14 | "origin": "Natural/Standard", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 15 | "coat": "Semi-long", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 16 | "pattern": "Bi- or tri-colored" +      | ^^^^^^^^^^^^^^^^^^^^ +      > 17 | }, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 18 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 19 | "breed": "American Curl", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 20 | "country": "United States", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 21 | "origin": "Mutation", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 22 | "coat": "Short/Long", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 23 | "pattern": "All" +      | ^^^^^^^^^^^^^^^^^^^^ +      > 24 | }, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 25 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 26 | "breed": "American Bobtail", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 27 | "country": "United States", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 28 | "origin": "Mutation", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 29 | "coat": "Short/Long", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 30 | "pattern": "All" +      | ^^^^^^^^^^^^^^^^^^^^ +      > 31 | }, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 32 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 33 | "breed": "American Shorthair", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 34 | "country": "United States", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 35 | "origin": "Natural", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 36 | "coat": "Short", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 37 | "pattern": "All but colorpoint" +      | ^^^^^^^^^^^^^^^^^^^^ +      > 38 | }, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 39 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 40 | "breed": "American Wirehair", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 41 | "country": "United States", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 42 | "origin": "Mutation", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 43 | "coat": "Rex", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 44 | "pattern": "All but colorpoint" +      | ^^^^^^^^^^^^^^^^^^^^ +      > 45 | }, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 46 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 47 | "breed": "Arabian Mau", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 48 | "country": "Arabian Peninsula", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 49 | "origin": "Natural", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 50 | "coat": "Short", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 51 | "pattern": "" +      | ^^^^^^^^^^^^^^^^^^^^ +      > 52 | }, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 53 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 54 | "breed": "Australian Mist", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 55 | "country": "Australia", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 56 | "origin": "Crossbreed", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 57 | "coat": "Short", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 58 | "pattern": "Spotted and Classic tabby" +      | ^^^^^^^^^^^^^^^^^^^^ +      > 59 | }, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 60 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 61 | "breed": "Asian", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 62 | "country": "developed in the United Kingdom (founding stock from Asia)", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 63 | "origin": "", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 64 | "coat": "Short", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 65 | "pattern": "Evenly solid" +      | ^^^^^^^^^^^^^^^^^^^^ +      > 66 | }, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 67 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 68 | "breed": "Asian Semi-longhair", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 69 | "country": "United Kingdom", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 70 | "origin": "Crossbreed", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 71 | "coat": "Semi-long", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 72 | "pattern": "Solid" +      | ^^^^^^^^^^^^^^^^^^^^ +      > 73 | }, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 74 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 75 | "breed": "Balinese", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 76 | "country": "developed in the United States (founding stock from Thailand)", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 77 | "origin": "Crossbreed", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 78 | "coat": "Long", +      | ^^^^^^^^^^^^^^^^^^^^ +      > 79 | "pattern": "Colorpoint" +      | ^^^^^^^^^^^^^^^^^^^^ +      > 80 | }, +      | ^^^^^^^^^^^^^^^^^^^^ +      > 81 | { +      | ^^^^^^^^^^^^^^^^^^^^ +      > 82 | "breed": "Bambino"," +`; diff --git a/__tests__/respect/cat-fact-api/auto-cat.yaml b/__tests__/respect/cat-fact-api/auto-cat.yaml new file mode 100644 index 0000000000..d3d8835106 --- /dev/null +++ b/__tests__/respect/cat-fact-api/auto-cat.yaml @@ -0,0 +1,21 @@ +arazzo: 1.0.1 +info: + title: Cat Facts API + version: '1.0' +sourceDescriptions: + - name: cats + type: openapi + url: cats.yaml +workflows: + - workflowId: get-breeds-workflow + steps: + - stepId: get-breeds-step + operationId: $sourceDescriptions.cats.getBreeds + - workflowId: get-fact-workflow + steps: + - stepId: get-fact-step + operationId: $sourceDescriptions.cats.getRandomFact + - workflowId: get-facts-workflow + steps: + - stepId: get-facts-step + operationId: $sourceDescriptions.cats.getFacts diff --git a/__tests__/respect/cat-fact-api/cat-fact-api.test.ts b/__tests__/respect/cat-fact-api/cat-fact-api.test.ts new file mode 100644 index 0000000000..ff08edd740 --- /dev/null +++ b/__tests__/respect/cat-fact-api/cat-fact-api.test.ts @@ -0,0 +1,14 @@ +import { join } from 'path'; +import { getParams, getCommandOutput, getFixturePath, cleanColors } from '../utils'; + +test('cats api test case', () => { + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'auto-cat.yaml'); + const args = getParams(indexEntryPoint, ['respect', fixturesPath]); + const result = cleanColors(getCommandOutput(args)); + + const lines = result.split('\n'); // Split the result into lines + const relevantPart = lines.slice(0, 200).join('\n'); // Extract the relevant lines + + expect(relevantPart).toMatchSnapshot(); +}); diff --git a/__tests__/respect/cat-fact-api/cats.yaml b/__tests__/respect/cat-fact-api/cats.yaml new file mode 100644 index 0000000000..7cda1c660c --- /dev/null +++ b/__tests__/respect/cat-fact-api/cats.yaml @@ -0,0 +1,133 @@ +openapi: 3.0.0 +info: + title: Cat Facts API + version: '1.0' +servers: + - url: https://catfact.ninja/ +paths: + '/breeds': + get: + tags: + - Breeds + summary: Get a list of breeds + description: Returns a a list of breeds + operationId: getBreeds + parameters: + - name: limit + in: query + description: limit the amount of results returned + required: false + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + '$ref': '#/components/schemas/Breed' + '/fact': + get: + tags: + - Facts + summary: Get Random Fact + description: Returns a random fact + operationId: getRandomFact + parameters: + - name: max_length + in: query + description: maximum length of returned fact + required: false + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/json: + schema: + '$ref': '#/components/schemas/CatFact' + '404': + description: Fact not found + '/facts': + get: + tags: + - Facts + summary: Get a list of facts + description: Returns a a list of facts + operationId: getFacts + parameters: + - name: max_length + in: query + description: maximum length of returned fact + required: false + schema: + type: integer + format: int64 + - name: limit + in: query + description: limit the amount of results returned + required: false + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + '$ref': '#/components/schemas/CatFact' +components: + schemas: + Breed: + title: Breed model + description: Breed + properties: + breed: + title: Breed + description: Breed + type: string + format: string + country: + title: Country + description: Country + type: string + format: string + origin: + title: Origin + description: Origin + type: string + format: string + coat: + title: Coat + description: Coat + type: string + format: string + pattern: + title: Pattern + description: Pattern + type: string + format: string + type: object + CatFact: + title: CatFact model + description: CatFact + properties: + fact: + title: Fact + description: Fact + type: string + format: string + length: + title: Length + description: Length + type: integer + format: int32 + type: object diff --git a/__tests__/respect/free-apis/__snapshots__/free-apis.test.ts.snap b/__tests__/respect/free-apis/__snapshots__/free-apis.test.ts.snap new file mode 100644 index 0000000000..f1bf2bf781 --- /dev/null +++ b/__tests__/respect/free-apis/__snapshots__/free-apis.test.ts.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`free apis test case 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow free.yaml / json + + ✓ GET https://jsonplaceholder.typicode.com/posts/1 - step get-first-post +    ✓ success criteria check +    ✓ success criteria check +    ✓ success criteria check +    ✓ success criteria check +    ✓ success criteria check + + ✓ GET https://jsonplaceholder.typicode.com/posts/wrong-id - step get-wrong-post +    ✓ success criteria check + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow free.yaml / xml + + ✗ POST http://restapi.adequateshop.com/api/Traveler - step post-traveler +    ✓ success criteria check +    ✓ success criteria check +    ✗ success criteria check + + +  Failed tests info: + +  Workflow name: xml + +    stepId - post-traveler +    ✗ success criteria check +      Checking simple criteria: {"condition":"$response.body == 'Pleae try with different email address!'"} +       +  Summary for free.yaml +   +  Workflows: 1 passed, 1 failed, 2 total +  Steps: 2 passed, 1 failed, 3 total +  Checks: 8 passed, 1 failed, 9 total +  Time: ms + + +┌───────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├───────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ x free.yaml │ 2 │ 1 │ 1 │ - │ - │ +└───────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + + + Tests exited with error +" +`; diff --git a/__tests__/respect/free-apis/free-apis.test.ts b/__tests__/respect/free-apis/free-apis.test.ts new file mode 100644 index 0000000000..c7ce026974 --- /dev/null +++ b/__tests__/respect/free-apis/free-apis.test.ts @@ -0,0 +1,11 @@ +import { join } from 'path'; +import { getParams, getCommandOutput } from '../utils'; + +test('free apis test case', () => { + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'free.yaml'); + const args = getParams(indexEntryPoint, ['respect', fixturesPath]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); +}); diff --git a/__tests__/respect/free-apis/free.yaml b/__tests__/respect/free-apis/free.yaml new file mode 100644 index 0000000000..0b33ae8c98 --- /dev/null +++ b/__tests__/respect/free-apis/free.yaml @@ -0,0 +1,49 @@ +arazzo: 1.0.1 +info: + title: Free APIs + version: '1.0' +sourceDescriptions: + - name: testing_acme + type: openapi + url: testing-acme.yaml +workflows: + - workflowId: json + steps: + - stepId: get-first-post + x-operation: + url: https://jsonplaceholder.typicode.com/posts/1 + method: get + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/id == 1 + - condition: $response.body#/title == 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit' + - condition: $response.body#/body == "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" + - condition: $response.body#/userId == 1 + - stepId: get-wrong-post + x-operation: + url: https://jsonplaceholder.typicode.com/posts/wrong-id + method: get + successCriteria: + - condition: $statusCode == 404 + - workflowId: xml + steps: + - stepId: post-traveler + x-operation: + url: http://restapi.adequateshop.com/api/Traveler + method: post + parameters: + - in: header + name: content-type + value: application/xml + requestBody: + payload: | + + + John + john344+1@gmail.com + Usa + + successCriteria: + - condition: $statusCode == 404 + - condition: $response.header.content-type == 'text/html; charset=us-ascii' + - condition: $response.body == 'Pleae try with different email address!' diff --git a/__tests__/respect/free-apis/testing-acme.yaml b/__tests__/respect/free-apis/testing-acme.yaml new file mode 100644 index 0000000000..e36d44fcd2 --- /dev/null +++ b/__tests__/respect/free-apis/testing-acme.yaml @@ -0,0 +1,764 @@ +openapi: 3.1.0 +servers: + - url: https://api.impossiblemissionsforce.com +info: + title: Learning API Demo + version: v1 + termsOfService: https://impossiblemissionsforce.com/terms + description: This is a mock API. It is for demo and learning purposes. The server URL is powered by a Remockly mock server. You can send curl requests and use the try it console to interact with the mock API. +tags: + - name: Activities + description: Endpoints related to learning activities, such as lessons, videos, and interactive exercises. Use these APIs to create, update, retrieve, and delete learning activities. + - name: Quizzes + description: Endpoints for managing quizzes and quiz questions. These APIs enable you to create, update, retrieve, and delete quizzes, as well as manage questions and options within quizzes. + - name: Badges + description: Endpoints for badge management, including creating, updating, retrieving, and deleting badges. Badges are awarded to learners based on their performance or progress in the learning system. + - name: Scores + description: Endpoints for handling learner scores on quizzes, activities, and assignments. Use these APIs to submit, retrieve, update, and delete scores for individual learners or groups. + - name: Checklists + description: Endpoints for managing checklists, which are lists of tasks or objectives learners must complete. These APIs enable you to create, update, retrieve, and delete checklists, as well as manage checklist items. + - name: Assignments + description: Endpoints related to assignments, including creating, updating, retrieving, and deleting assignments. Assignments may include projects, essays, or other tasks that learners are required to complete. + - name: Reports + description: Endpoints for generating and retrieving reports on learner progress, activity completion, and overall performance. Use these APIs to access and analyze data on individual learners or groups, as well as export reports for further analysis. + - name: Other + description: Other endpoints are described here. +paths: + /: + get: + tags: + - Other + operationId: GetRoot + summary: Get API info + responses: + '200': + description: OK. + content: + application/json: + example: This is a demo API. + '400': + $ref: '#/components/responses/badRequest' + /activities: + post: + tags: + - Activities + operationId: PostActivity + summary: Record activity + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/activity' + responses: + '204': + description: OK (no content). + '400': + $ref: '#/components/responses/badRequest' + /quizzes: + post: + tags: + - Quizzes + operationId: PostQuiz + summary: Create quiz + parameters: # Added for implicit content type headers test + - name: Content-Type + in: header + required: true + example: application/original + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/quiz' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/quiz' + '400': + $ref: '#/components/responses/badRequest' + get: + tags: + - Quizzes + operationId: GetQuizzes + summary: Get quizzes + responses: + '200': + description: Retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/quiz' + '400': + $ref: '#/components/responses/badRequest' + /checklists: + post: + tags: + - Checklists + operationId: PostChecklist + summary: Create checklist + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/checklist' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/checklist' + '400': + $ref: '#/components/responses/badRequest' + get: + tags: + - Checklists + operationId: GetChecklists + summary: Get checklists + responses: + '200': + description: Retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/checklist' + '400': + $ref: '#/components/responses/badRequest' + /badges: + post: + tags: + - Badges + operationId: PostBadge + summary: Create badge + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/badge' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/badge' + '400': + $ref: '#/components/responses/badRequest' + get: + tags: + - Badges + operationId: GetBadges + summary: Get badges + responses: + '200': + description: Retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/badge' + '400': + $ref: '#/components/responses/badRequest' + /assignments: + post: + tags: + - Assignments + operationId: PostAssignment + summary: Create assignment + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/assignment' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/assignment' + '400': + $ref: '#/components/responses/badRequest' + get: + tags: + - Assignments + operationId: GetAssignments + summary: Get assignments + responses: + '200': + description: OK. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/assignment' + '400': + $ref: '#/components/responses/badRequest' + /scores: + get: + tags: + - Scores + operationId: GetScores + summary: Get scores + parameters: + - name: user + in: query + schema: + type: string + examples: + - abc123 + - name: quiz + in: query + schema: + type: string + examples: + - Who we are + responses: + '200': + description: Retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/score' + '400': + $ref: '#/components/responses/badRequest' + /users: + get: + tags: + - Reports + operationId: GetUserList + summary: Get users + responses: + '200': + description: OK. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/userSummary' + '400': + $ref: '#/components/responses/badRequest' + /users/{id}: + get: + tags: + - Reports + operationId: GetUserActivity + summary: Get user activity + parameters: + - name: id + in: path + required: true + schema: + type: string + examples: + - abc123 + responses: + '200': + description: OK. + content: + application/json: + schema: + $ref: '#/components/schemas/userDetail' + '400': + $ref: '#/components/responses/badRequest' + /reports/summaries: + get: + tags: + - Reports + operationId: GetSummaryReport + summary: Get summary report + parameters: + - name: startDate + in: query + required: true + schema: + type: string + format: date-time + examples: + - '2023-01-01T00:00:00Z' + - name: endDate + in: query + required: true + schema: + type: string + format: date-time + examples: + - '2023-02-01T00:00:00Z' + responses: + '200': + description: OK. + content: + application/json: + schema: + $ref: '#/components/schemas/summaryReport' + '400': + $ref: '#/components/responses/badRequest' +security: + - imfKey: [] +components: + responses: + badRequest: + description: Bad request. + content: + application/problem+json: + schema: + type: object + additionalProperties: true + minProperties: 1 + description: Problem Details JSON Object [[RFC7807](https://tools.ietf.org/html/rfc7807)]. + properties: + type: + type: string + description: URI reference [[RFC3986](https://tools.ietf.org/html/rfc3986)] that identifies the problem type. It should provide human-readable documentation for the problem type. When this member is not present, its value is assumed to be "about:blank". + format: uri + title: + type: string + description: Short, human-readable summary of the problem type. It SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization. + status: + type: integer + description: HTTP status code. + minimum: 400 + maximum: 599 + detail: + type: string + description: Human-readable explanation specific to this occurrence of the problem. + instance: + type: string + description: URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. + securitySchemes: + imfKey: + type: apiKey + in: header + name: IMF-KEY + schemas: + activity: + type: object + title: Activity + properties: + id: + type: string + readOnly: true + examples: + - id_abc987def123 + data: + type: object + properties: + type: + type: string + examples: + - quiz + name: + type: string + examples: + - Onboarding Part 1 + - Who we are + item: + type: string + examples: + - Sign agreements + - What is our mission? + answer: + type: + - string + - 'null' + examples: + - a + additionalProperties: false + metadata: + type: object + properties: + user: + type: string + examples: + - abc123 + domain: + type: string + examples: + - https://impossiblemissionsforce.com + format: url + path: + type: string + examples: + - /security/network-security/ + timestamp: + type: string + examples: + - '2023-02-19T09:31:27Z' + format: date-time + additionalProperties: false + additionalProperties: false + checklist: + type: object + title: Checklist + properties: + id: + type: string + readOnly: true + examples: + - cklst_abc987def123 + name: + type: string + examples: + - Onboarding Part 1 + items: + type: array + items: + type: string + examples: + - Sign agreements + - Set up accounts + - Set up meetings with colleagues + - Learn who we are + createdAt: + type: string + examples: + - '2023-02-19T09:31:27Z' + format: date-time + updatedAt: + type: string + examples: + - '2023-02-19T09:31:27Z' + format: date-time + additionalProperties: false + quiz: + type: object + properties: + id: + type: string + readOnly: true + examples: + - quiz_abc987def123 + name: + type: string + examples: + - Who we are + passingScore: + type: number + examples: + - 0.8 + questions: + type: array + items: + type: object + properties: + type: + type: string + enum: + - choice + - blank + examples: + - choice + question: + type: string + examples: + - What is our mission? + - What year were we founded? + choices: + type: + - object + - 'null' + properties: + a: + type: string + examples: + - Multinational espionage agency + - '1848' + b: + type: string + examples: + - Prevent manmade catastrophes + - '1945' + c: + type: string + examples: + - Do amazing stunts + - '1977' + d: + type: string + examples: + - '2023' + additionalProperties: false + correctAnswer: + type: string + examples: + - b + - b + additionalProperties: false + createdAt: + type: string + readOnly: true + examples: + - '2023-02-19T10:13:21Z' + format: date-time + updatedAt: + type: string + readOnly: true + examples: + - '2023-02-20T02:36:02Z' + format: date-time + additionalProperties: false + assignment: + type: object + required: + - user + - type + - name + properties: + id: + type: string + readOnly: true + examples: + - asmt_atskthask23498uas2 + user: + type: string + examples: + - abc123 + type: + type: string + examples: + - quiz + name: + type: string + examples: + - Who we are + maxAttempts: + type: integer + examples: + - 3 + status: + type: string + readOnly: true + examples: + - done + assignedAt: + type: string + readOnly: true + examples: + - '2023-02-19T10:56:21Z' + format: date-time + deadline: + type: string + examples: + - '2023-02-25T00:00:00Z' + format: date-time + completedAt: + type: string + readOnly: true + examples: + - '2023-02-19T14:52:21Z' + format: date-time + progress: + type: number + readOnly: true + examples: + - 1 + data: + type: object + readOnly: true + additionalProperties: false + score: + type: object + properties: + id: + type: string + readOnly: true + examples: + - score_abc987def123 + user: + type: string + examples: + - abc123 + score: + type: number + examples: + - 0.85 + quiz: + type: string + examples: + - Who we are + attempts: + type: integer + examples: + - 1 + breakdown: + type: array + items: + type: object + properties: + question: + type: string + examples: + - What is our mission? + choices: + type: object + properties: + a: + type: string + examples: + - Multinational espionage agency + b: + type: string + examples: + - Prevent manmade catastrophes + c: + type: string + examples: + - Do amazing stunts + additionalProperties: false + correctAnswer: + type: string + examples: + - b + answer: + type: string + examples: + - a + answeredAt: + type: string + format: date-time + examples: + - '2023-02-20T03:45:08Z' + additionalProperties: false + additionalProperties: false + badge: + type: object + properties: + id: + type: string + readOnly: true + examples: + - badge_abc987def123 + name: + type: string + examples: + - Explorer + icon: + type: string + examples: + - /images/explorer-icon.png + requirements: + type: array + items: + type: object + properties: + type: + type: string + examples: + - quiz + - checklist + name: + type: string + examples: + - Who we are + - Onboarding Part 1 + minScore: + type: number + examples: + - 0.85 + maxAttempts: + type: integer + examples: + - 1 + status: + type: string + examples: + - done + additionalProperties: false + createdAt: + type: string + examples: + - '2023-02-19T11:21:36Z' + format: date-time + updatedAt: + type: string + examples: + - '2023-02-19T11:21:36Z' + format: date-time + additionalProperties: false + userDetail: + type: object + properties: + id: + type: string + examples: + - usr_abc123 + badges: + type: array + items: + $ref: '#/components/schemas/badge' + assignments: + type: array + items: + $ref: '#/components/schemas/assignment' + activity: + type: array + items: + $ref: '#/components/schemas/activity' + additionalProperties: false + userSummary: + type: object + properties: + id: + type: string + examples: + - usr_abc123 + badgesCount: + type: integer + assignments: + type: integer + activity: + type: integer + additionalProperties: false + summaryReport: + type: object + properties: + id: + type: string + readOnly: true + examples: + - rpt_abc987def123 + startDate: + type: string + examples: + - '2023-01-01T00:00:00Z' + format: date-time + endDate: + type: string + examples: + - '2023-02-01T00:00:00Z' + format: date-time + users: + type: integer + examples: + - 35 + attempts: + type: integer + examples: + - 45 + quizzes: + type: integer + examples: + - 3 + passRate: + type: number + examples: + - 0.73 + checklists: + type: integer + examples: + - 2 + additionalProperties: false diff --git a/__tests__/respect/implicit/__snapshots__/implicit.test.ts.snap b/__tests__/respect/implicit/__snapshots__/implicit.test.ts.snap new file mode 100644 index 0000000000..1e8b15b6ca --- /dev/null +++ b/__tests__/respect/implicit/__snapshots__/implicit.test.ts.snap @@ -0,0 +1,99 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should implicitly add content type header based on requestBody.content field (the first one) if such does not specified 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow implicit.yaml / implicit-content-type + + ✓ POST /activities - step post-activities + +    Request URL: https://api.impossiblemissionsforce.com/activities +    Request Headers: +      content-type: application/json +      accept: application/problem+json +      imf-key: test-key +    Request Body: +      { +       "data": { +       "type": "quiz", +       "name": "Onboarding Part 1", +       "item": "Sign agreements", +       "answer": "a" +       }, +       "metadata": { +       "user": "abc123", +       "domain": "https://impossiblemissionsforce.com", +       "path": "/security/network-security/", +       "timestamp": "2023-02-19T09:31:27Z" +       } +      } + + +    Response status code: 204 +    Response time: ms +    Response Body: +      {} + +    ✓ status code check (Response code 204 matches one of description codes: [204, 400]) + + ✓ POST /quizzes - step post-quizzes + +    Request URL: https://api.impossiblemissionsforce.com/quizzes +    Request Headers: +      content-type: application/original +      accept: application/json, application/problem+json +      imf-key: test-key +    Request Body: +      { +       "name": "Who we are", +       "passingScore": 0.8, +       "questions": [ +       { +       "type": "choice", +       "question": "What is our mission?", +       "choices": { +       "a": "Multinational espionage agency", +       "b": "Prevent manmade catastrophes", +       "c": "Do amazing stunts", +       "d": "2023" +       }, +       "correctAnswer": "b" +       } +       ] +      } + + +    Response status code: 400 +    Response time: ms +    Response Body: +      { +       "type": "http://example.com", +       "title": "Media type \\"application/original\\" is not supported", +       "status": 400, +       "detail": "string", +       "instance": "string" +      } + +    ✓ success criteria check +    ✓ status code check (Response code 400 matches one of description codes: [201, 400]) +    ✓ content-type check +    ✓ schema check + + +  Summary for implicit.yaml +   +  Workflows: 1 passed, 1 total +  Steps: 2 passed, 2 total +  Checks: 5 passed, 5 total +  Time: ms + + +┌───────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├───────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ ✓ implicit.yaml │ 1 │ 1 │ - │ - │ - │ +└───────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + +" +`; diff --git a/__tests__/respect/implicit/implicit.test.ts b/__tests__/respect/implicit/implicit.test.ts new file mode 100644 index 0000000000..cb46fa52ca --- /dev/null +++ b/__tests__/respect/implicit/implicit.test.ts @@ -0,0 +1,16 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +test('should implicitly add content type header based on requestBody.content field (the first one) if such does not specified', () => { + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'implicit.yaml'); + const args = getParams(indexEntryPoint, [ + 'respect', + fixturesPath, + '-w=implicit-content-type', + '--verbose', + ]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); +}); diff --git a/__tests__/respect/implicit/implicit.yaml b/__tests__/respect/implicit/implicit.yaml new file mode 100644 index 0000000000..6baddf19c6 --- /dev/null +++ b/__tests__/respect/implicit/implicit.yaml @@ -0,0 +1,25 @@ +arazzo: 1.0.1 +info: + title: Learning API Demo + version: v1 +sourceDescriptions: + - name: testing_acme + type: openapi + url: testing-acme.yaml + x-serverUrl: $servers.0.url + +workflows: + - workflowId: implicit-content-type + parameters: + - in: header + name: IMF-KEY + value: test-key + steps: + - stepId: post-activities + description: 'Should add application/json content-type header implicitly' + operationId: PostActivity + - stepId: post-quizzes + description: 'Should use original content-type header if such provided' + operationId: PostQuiz + successCriteria: + - condition: $request.header.content-type == 'application/original' diff --git a/__tests__/respect/implicit/testing-acme.yaml b/__tests__/respect/implicit/testing-acme.yaml new file mode 100644 index 0000000000..e36d44fcd2 --- /dev/null +++ b/__tests__/respect/implicit/testing-acme.yaml @@ -0,0 +1,764 @@ +openapi: 3.1.0 +servers: + - url: https://api.impossiblemissionsforce.com +info: + title: Learning API Demo + version: v1 + termsOfService: https://impossiblemissionsforce.com/terms + description: This is a mock API. It is for demo and learning purposes. The server URL is powered by a Remockly mock server. You can send curl requests and use the try it console to interact with the mock API. +tags: + - name: Activities + description: Endpoints related to learning activities, such as lessons, videos, and interactive exercises. Use these APIs to create, update, retrieve, and delete learning activities. + - name: Quizzes + description: Endpoints for managing quizzes and quiz questions. These APIs enable you to create, update, retrieve, and delete quizzes, as well as manage questions and options within quizzes. + - name: Badges + description: Endpoints for badge management, including creating, updating, retrieving, and deleting badges. Badges are awarded to learners based on their performance or progress in the learning system. + - name: Scores + description: Endpoints for handling learner scores on quizzes, activities, and assignments. Use these APIs to submit, retrieve, update, and delete scores for individual learners or groups. + - name: Checklists + description: Endpoints for managing checklists, which are lists of tasks or objectives learners must complete. These APIs enable you to create, update, retrieve, and delete checklists, as well as manage checklist items. + - name: Assignments + description: Endpoints related to assignments, including creating, updating, retrieving, and deleting assignments. Assignments may include projects, essays, or other tasks that learners are required to complete. + - name: Reports + description: Endpoints for generating and retrieving reports on learner progress, activity completion, and overall performance. Use these APIs to access and analyze data on individual learners or groups, as well as export reports for further analysis. + - name: Other + description: Other endpoints are described here. +paths: + /: + get: + tags: + - Other + operationId: GetRoot + summary: Get API info + responses: + '200': + description: OK. + content: + application/json: + example: This is a demo API. + '400': + $ref: '#/components/responses/badRequest' + /activities: + post: + tags: + - Activities + operationId: PostActivity + summary: Record activity + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/activity' + responses: + '204': + description: OK (no content). + '400': + $ref: '#/components/responses/badRequest' + /quizzes: + post: + tags: + - Quizzes + operationId: PostQuiz + summary: Create quiz + parameters: # Added for implicit content type headers test + - name: Content-Type + in: header + required: true + example: application/original + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/quiz' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/quiz' + '400': + $ref: '#/components/responses/badRequest' + get: + tags: + - Quizzes + operationId: GetQuizzes + summary: Get quizzes + responses: + '200': + description: Retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/quiz' + '400': + $ref: '#/components/responses/badRequest' + /checklists: + post: + tags: + - Checklists + operationId: PostChecklist + summary: Create checklist + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/checklist' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/checklist' + '400': + $ref: '#/components/responses/badRequest' + get: + tags: + - Checklists + operationId: GetChecklists + summary: Get checklists + responses: + '200': + description: Retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/checklist' + '400': + $ref: '#/components/responses/badRequest' + /badges: + post: + tags: + - Badges + operationId: PostBadge + summary: Create badge + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/badge' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/badge' + '400': + $ref: '#/components/responses/badRequest' + get: + tags: + - Badges + operationId: GetBadges + summary: Get badges + responses: + '200': + description: Retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/badge' + '400': + $ref: '#/components/responses/badRequest' + /assignments: + post: + tags: + - Assignments + operationId: PostAssignment + summary: Create assignment + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/assignment' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/assignment' + '400': + $ref: '#/components/responses/badRequest' + get: + tags: + - Assignments + operationId: GetAssignments + summary: Get assignments + responses: + '200': + description: OK. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/assignment' + '400': + $ref: '#/components/responses/badRequest' + /scores: + get: + tags: + - Scores + operationId: GetScores + summary: Get scores + parameters: + - name: user + in: query + schema: + type: string + examples: + - abc123 + - name: quiz + in: query + schema: + type: string + examples: + - Who we are + responses: + '200': + description: Retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/score' + '400': + $ref: '#/components/responses/badRequest' + /users: + get: + tags: + - Reports + operationId: GetUserList + summary: Get users + responses: + '200': + description: OK. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/userSummary' + '400': + $ref: '#/components/responses/badRequest' + /users/{id}: + get: + tags: + - Reports + operationId: GetUserActivity + summary: Get user activity + parameters: + - name: id + in: path + required: true + schema: + type: string + examples: + - abc123 + responses: + '200': + description: OK. + content: + application/json: + schema: + $ref: '#/components/schemas/userDetail' + '400': + $ref: '#/components/responses/badRequest' + /reports/summaries: + get: + tags: + - Reports + operationId: GetSummaryReport + summary: Get summary report + parameters: + - name: startDate + in: query + required: true + schema: + type: string + format: date-time + examples: + - '2023-01-01T00:00:00Z' + - name: endDate + in: query + required: true + schema: + type: string + format: date-time + examples: + - '2023-02-01T00:00:00Z' + responses: + '200': + description: OK. + content: + application/json: + schema: + $ref: '#/components/schemas/summaryReport' + '400': + $ref: '#/components/responses/badRequest' +security: + - imfKey: [] +components: + responses: + badRequest: + description: Bad request. + content: + application/problem+json: + schema: + type: object + additionalProperties: true + minProperties: 1 + description: Problem Details JSON Object [[RFC7807](https://tools.ietf.org/html/rfc7807)]. + properties: + type: + type: string + description: URI reference [[RFC3986](https://tools.ietf.org/html/rfc3986)] that identifies the problem type. It should provide human-readable documentation for the problem type. When this member is not present, its value is assumed to be "about:blank". + format: uri + title: + type: string + description: Short, human-readable summary of the problem type. It SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization. + status: + type: integer + description: HTTP status code. + minimum: 400 + maximum: 599 + detail: + type: string + description: Human-readable explanation specific to this occurrence of the problem. + instance: + type: string + description: URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. + securitySchemes: + imfKey: + type: apiKey + in: header + name: IMF-KEY + schemas: + activity: + type: object + title: Activity + properties: + id: + type: string + readOnly: true + examples: + - id_abc987def123 + data: + type: object + properties: + type: + type: string + examples: + - quiz + name: + type: string + examples: + - Onboarding Part 1 + - Who we are + item: + type: string + examples: + - Sign agreements + - What is our mission? + answer: + type: + - string + - 'null' + examples: + - a + additionalProperties: false + metadata: + type: object + properties: + user: + type: string + examples: + - abc123 + domain: + type: string + examples: + - https://impossiblemissionsforce.com + format: url + path: + type: string + examples: + - /security/network-security/ + timestamp: + type: string + examples: + - '2023-02-19T09:31:27Z' + format: date-time + additionalProperties: false + additionalProperties: false + checklist: + type: object + title: Checklist + properties: + id: + type: string + readOnly: true + examples: + - cklst_abc987def123 + name: + type: string + examples: + - Onboarding Part 1 + items: + type: array + items: + type: string + examples: + - Sign agreements + - Set up accounts + - Set up meetings with colleagues + - Learn who we are + createdAt: + type: string + examples: + - '2023-02-19T09:31:27Z' + format: date-time + updatedAt: + type: string + examples: + - '2023-02-19T09:31:27Z' + format: date-time + additionalProperties: false + quiz: + type: object + properties: + id: + type: string + readOnly: true + examples: + - quiz_abc987def123 + name: + type: string + examples: + - Who we are + passingScore: + type: number + examples: + - 0.8 + questions: + type: array + items: + type: object + properties: + type: + type: string + enum: + - choice + - blank + examples: + - choice + question: + type: string + examples: + - What is our mission? + - What year were we founded? + choices: + type: + - object + - 'null' + properties: + a: + type: string + examples: + - Multinational espionage agency + - '1848' + b: + type: string + examples: + - Prevent manmade catastrophes + - '1945' + c: + type: string + examples: + - Do amazing stunts + - '1977' + d: + type: string + examples: + - '2023' + additionalProperties: false + correctAnswer: + type: string + examples: + - b + - b + additionalProperties: false + createdAt: + type: string + readOnly: true + examples: + - '2023-02-19T10:13:21Z' + format: date-time + updatedAt: + type: string + readOnly: true + examples: + - '2023-02-20T02:36:02Z' + format: date-time + additionalProperties: false + assignment: + type: object + required: + - user + - type + - name + properties: + id: + type: string + readOnly: true + examples: + - asmt_atskthask23498uas2 + user: + type: string + examples: + - abc123 + type: + type: string + examples: + - quiz + name: + type: string + examples: + - Who we are + maxAttempts: + type: integer + examples: + - 3 + status: + type: string + readOnly: true + examples: + - done + assignedAt: + type: string + readOnly: true + examples: + - '2023-02-19T10:56:21Z' + format: date-time + deadline: + type: string + examples: + - '2023-02-25T00:00:00Z' + format: date-time + completedAt: + type: string + readOnly: true + examples: + - '2023-02-19T14:52:21Z' + format: date-time + progress: + type: number + readOnly: true + examples: + - 1 + data: + type: object + readOnly: true + additionalProperties: false + score: + type: object + properties: + id: + type: string + readOnly: true + examples: + - score_abc987def123 + user: + type: string + examples: + - abc123 + score: + type: number + examples: + - 0.85 + quiz: + type: string + examples: + - Who we are + attempts: + type: integer + examples: + - 1 + breakdown: + type: array + items: + type: object + properties: + question: + type: string + examples: + - What is our mission? + choices: + type: object + properties: + a: + type: string + examples: + - Multinational espionage agency + b: + type: string + examples: + - Prevent manmade catastrophes + c: + type: string + examples: + - Do amazing stunts + additionalProperties: false + correctAnswer: + type: string + examples: + - b + answer: + type: string + examples: + - a + answeredAt: + type: string + format: date-time + examples: + - '2023-02-20T03:45:08Z' + additionalProperties: false + additionalProperties: false + badge: + type: object + properties: + id: + type: string + readOnly: true + examples: + - badge_abc987def123 + name: + type: string + examples: + - Explorer + icon: + type: string + examples: + - /images/explorer-icon.png + requirements: + type: array + items: + type: object + properties: + type: + type: string + examples: + - quiz + - checklist + name: + type: string + examples: + - Who we are + - Onboarding Part 1 + minScore: + type: number + examples: + - 0.85 + maxAttempts: + type: integer + examples: + - 1 + status: + type: string + examples: + - done + additionalProperties: false + createdAt: + type: string + examples: + - '2023-02-19T11:21:36Z' + format: date-time + updatedAt: + type: string + examples: + - '2023-02-19T11:21:36Z' + format: date-time + additionalProperties: false + userDetail: + type: object + properties: + id: + type: string + examples: + - usr_abc123 + badges: + type: array + items: + $ref: '#/components/schemas/badge' + assignments: + type: array + items: + $ref: '#/components/schemas/assignment' + activity: + type: array + items: + $ref: '#/components/schemas/activity' + additionalProperties: false + userSummary: + type: object + properties: + id: + type: string + examples: + - usr_abc123 + badgesCount: + type: integer + assignments: + type: integer + activity: + type: integer + additionalProperties: false + summaryReport: + type: object + properties: + id: + type: string + readOnly: true + examples: + - rpt_abc987def123 + startDate: + type: string + examples: + - '2023-01-01T00:00:00Z' + format: date-time + endDate: + type: string + examples: + - '2023-02-01T00:00:00Z' + format: date-time + users: + type: integer + examples: + - 35 + attempts: + type: integer + examples: + - 45 + quizzes: + type: integer + examples: + - 3 + passRate: + type: number + examples: + - 0.73 + checklists: + type: integer + examples: + - 2 + additionalProperties: false diff --git a/__tests__/respect/inputs-with-cli-and-env/__snapshots__/inputs-with-cli-and-env.test.ts.snap b/__tests__/respect/inputs-with-cli-and-env/__snapshots__/inputs-with-cli-and-env.test.ts.snap new file mode 100644 index 0000000000..0c765b1be0 --- /dev/null +++ b/__tests__/respect/inputs-with-cli-and-env/__snapshots__/inputs-with-cli-and-env.test.ts.snap @@ -0,0 +1,152 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should use inputs from CLI and env 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow inputs-with-cli-and-env.yaml / get-museum-hours + + ✓ GET /museum-hours - step get-museum-hours + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/museum-hours +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "date": "2023-09-11", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-12", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-13", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-14", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-15", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       }, +       { +       "date": "2023-09-18", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-19", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-20", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-21", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-22", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       } +      ] + +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + Running step buy-ticket workflow $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + + ✓ POST /tickets - step buy-tickets +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow inputs-with-cli-and-env.yaml / events-crud + + ✓ POST /special-events - step create-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      content-type: application/json +      accept: application/json, application/problem+json +      authorization: Basic Og== +      username-token: Basic Og== +    Request Body: +      { +       "additionalName": "John", +       "additionalPassword": "password", +       "additionalEnv": "Basic Og==", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + + +    Response status code: 201 +    Response time: ms +    Response Body: +      { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +  Summary for inputs-with-cli-and-env.yaml +   +  Workflows: 2 passed, 2 total +  Steps: 3 passed, 3 total +  Checks: 13 passed, 13 total +  Time: ms + + +┌──────────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├──────────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ ✓ inputs-with-cli-and-env.yaml │ 2 │ 2 │ - │ - │ - │ +└──────────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + +" +`; diff --git a/__tests__/respect/inputs-with-cli-and-env/inputs-with-cli-and-env.test.ts b/__tests__/respect/inputs-with-cli-and-env/inputs-with-cli-and-env.test.ts new file mode 100644 index 0000000000..1465409874 --- /dev/null +++ b/__tests__/respect/inputs-with-cli-and-env/inputs-with-cli-and-env.test.ts @@ -0,0 +1,21 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +test('should use inputs from CLI and env', () => { + process.env.AUTH_TOKEN = 'Basic Og=='; + + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'inputs-with-cli-and-env.yaml'); + const args = getParams(indexEntryPoint, [ + 'respect', + fixturesPath, + '--verbose', + '--input', + '{"username":"John","password":"password"}', + ]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); + + delete process.env.AUTH_TOKEN; +}); diff --git a/__tests__/respect/inputs-with-cli-and-env/inputs-with-cli-and-env.yaml b/__tests__/respect/inputs-with-cli-and-env/inputs-with-cli-and-env.yaml new file mode 100644 index 0000000000..87986df7a0 --- /dev/null +++ b/__tests__/respect/inputs-with-cli-and-env/inputs-with-cli-and-env.yaml @@ -0,0 +1,94 @@ +arazzo: 1.0.1 +info: + title: Test inputs with CLI and env + description: >- + This test demonstrates how to use inputs and environment variables in an API test. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + inputs: + type: object + properties: + env: + type: object + properties: + AUTH_TOKEN: + type: string + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: $inputs.env.AUTH_TOKEN + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body + - stepId: buy-ticket + description: >- + Buy a ticket for the museum by calling an external workflow from another Arazzo file. + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + outputs: + ticketId: $outputs.ticketId + - workflowId: events-crud + inputs: + type: object + properties: + env: + type: object + properties: + AUTH_TOKEN: + type: string + username: + type: string + password: + type: string + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: create-event + parameters: + - in: header + name: username-token + value: $inputs.env.AUTH_TOKEN + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + requestBody: + payload: + additionalName: $inputs.username + additionalPassword: $inputs.password + additionalEnv: $inputs.env.AUTH_TOKEN + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 201 + - context: $response.body + condition: $.name == 'Mermaid Treasure Identification and Analysis' + type: jsonpath + outputs: + createdEventId: $response.body#/eventId + name: $response.body#/name diff --git a/__tests__/respect/inputs-with-cli-and-env/museum-api.yaml b/__tests__/respect/inputs-with-cli-and-env/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/inputs-with-cli-and-env/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/inputs-with-cli-and-env/museum-tickets.yaml b/__tests__/respect/inputs-with-cli-and-env/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/inputs-with-cli-and-env/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/local-json-server/__ignored_snapshots__/local-json-server.test.ts.ignored-snap b/__tests__/respect/local-json-server/__ignored_snapshots__/local-json-server.test.ts.ignored-snap new file mode 100644 index 0000000000..b5a6dab4d0 --- /dev/null +++ b/__tests__/respect/local-json-server/__ignored_snapshots__/local-json-server.test.ts.ignored-snap @@ -0,0 +1,95 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`local-json-server test case 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow local-json-server.yaml / before + + ✓ GET /items - step get-all-items-before +    ✓ status code check (Response code 200 matches one of description codes: [200]) +    ✓ content-type check +    ✓ schema check + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow local-json-server.yaml / crud-items + + ✓ POST /items - step add-item +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201]) +    ✓ content-type check +    ✓ schema check + + ✗ GET /items - step get-all-items-again +    ✗ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200]) +    ✓ content-type check +    ✓ schema check + + ✓ GET /items/{id} - step get-item-by-id +    ✓ success criteria check +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 404]) +    ✓ content-type check +    ✓ schema check + + ✓ DELETE /items/{id} - step drop-item +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200]) + + ✓ GET /items/{id} - step get-after-deleting +    ✓ success criteria check +    ✓ status code check (Response code 404 matches one of description codes: [200, 404]) +    ✓ content-type check + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow local-json-server.yaml / auto-inherit + + ✓ POST /items - step add-item-auto-inherit +    ✓ status code check (Response code 201 matches one of description codes: [201]) +    ✓ content-type check +    ✓ schema check + + ✓ GET /items - step get-all-items +    ✓ status code check (Response code 200 matches one of description codes: [200]) +    ✓ content-type check +    ✓ schema check + + ✓ GET /items/{id} - step get-item-by-id-auto-inherit +    ✓ status code check (Response code 404 matches one of description codes: [200, 404]) +    ✓ content-type check + + ✓ DELETE /items/{id} - step drop-item-auto-inherit +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200]) + + +  Failed tests info: + +  Workflow name: crud-items + +    stepId - get-all-items-again +    ✗ success criteria check +      Checking simple criteria: {"condition":"$response.body#/0/value == 100"} +       +  Summary for local-json-server.yaml +   +  Workflows: 2 passed, 1 failed, 3 total +  Steps: 9 passed, 1 failed, 10 total +  Checks: 31 passed, 1 failed, 32 total +  Time: ms + + +┌────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ x local-json-server.yaml │ 3 │ 2 │ 1 │ - │ - │ +└────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + + + Tests exited with error +" +`; diff --git a/__tests__/respect/local-json-server/fake-db.json b/__tests__/respect/local-json-server/fake-db.json new file mode 100644 index 0000000000..2feb21015d --- /dev/null +++ b/__tests__/respect/local-json-server/fake-db.json @@ -0,0 +1,3 @@ +{ + "items": [] +} diff --git a/__tests__/respect/local-json-server/json-server-openapi.yaml b/__tests__/respect/local-json-server/json-server-openapi.yaml new file mode 100644 index 0000000000..e3e4a1c1c3 --- /dev/null +++ b/__tests__/respect/local-json-server/json-server-openapi.yaml @@ -0,0 +1,112 @@ +openapi: 3.1.0 +info: + title: Fake JSON Server + version: 1.0.0 +servers: + - url: http://localhost:3000 +security: [] +components: + schemas: + Item: + type: object + properties: + id: + type: string + value: + type: number + required: + - id + parameters: + ContentType: + name: Content-Type + in: header + required: true + schema: + type: string + example: application/json + ItemId: + name: id + in: path + schema: + type: string + format: uuid + example: some-item-id + required: true + +paths: + /items: + get: + operationId: list-items + summary: Retrieve all items. + responses: + 200: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Item' + + post: + operationId: create-item + summary: Create a new item. + parameters: + - $ref: '#/components/parameters/ContentType' + requestBody: + content: + application/json: + examples: + 201: + value: { value: 42 } + responses: + 201: + content: + application/json: + schema: + $ref: '#/components/schemas/Item' + + /items/{id}: + # TODO: make parameters inherited from the top level + # parameters: + # - name: id + # in: path + # schema: + # type: string + # format: uuid + # example: some-item-id + # required: true + + get: + operationId: get-by-id + summary: Get an item by id. + parameters: + - $ref: '#/components/parameters/ItemId' + responses: + 200: + description: Found an item. + content: + application/json: + schema: + $ref: '#/components/schemas/Item' + + 404: + description: Not found. + content: + text/plain: + examples: + response: + value: OK + application/json: + schema: + type: object + properties: {} + additionalProperties: false + + delete: + operationId: remove-item + summary: Remove an item by id. + parameters: + - $ref: '#/components/parameters/ItemId' + responses: + 200: + description: Successfully deleted. diff --git a/__tests__/respect/local-json-server/local-json-server.test.ts b/__tests__/respect/local-json-server/local-json-server.test.ts new file mode 100644 index 0000000000..519c167e39 --- /dev/null +++ b/__tests__/respect/local-json-server/local-json-server.test.ts @@ -0,0 +1,16 @@ +// import { getParams, getCommandOutput } from '../utils'; +// import { join } from 'path'; + +// test('local-json-server test case', () => { +// const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); +// const fixturesPath = join(__dirname, 'local-json-server.yaml'); +// const args = getParams(indexEntryPoint, ['respect', fixturesPath]); + +// const result = getCommandOutput(args); +// expect(result).toMatchSnapshot(); +// }); + +test('local-json-server test case', () => { + //TODO: Remove.Disable this test until json-server is added to the e2e flow. + expect(true).toBe(true); +}); diff --git a/__tests__/respect/local-json-server/local-json-server.yaml b/__tests__/respect/local-json-server/local-json-server.yaml new file mode 100644 index 0000000000..231af37824 --- /dev/null +++ b/__tests__/respect/local-json-server/local-json-server.yaml @@ -0,0 +1,82 @@ +arazzo: 1.0.1 +info: + title: Fake JSON Server + version: 1.0.0 +sourceDescriptions: + - name: json-server-openapi + type: openapi + url: json-server-openapi.yaml +workflows: + - workflowId: before + steps: + - stepId: get-all-items-before + operationId: list-items + - workflowId: crud-items + steps: + - stepId: add-item + operationId: create-item + requestBody: + payload: + value: '$faker.number.integer({ min: 1, max: 10 })' + parameters: + - in: header + name: Content-Type + value: application/json + successCriteria: + - condition: $statusCode == 201 + outputs: + createdItemId: $response.body#/id + createdItemValue: $response.body#/value + - stepId: get-all-items-again + operationId: list-items + successCriteria: + # Expecting wrong lvalue == 100 + - condition: $response.body#/0/value == 100 + + - stepId: get-item-by-id + operationId: get-by-id + parameters: + - in: path + name: id + value: $steps.add-item.outputs.createdItemId + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/id == $steps.add-item.outputs.createdItemId + - condition: $response.body#/value == $steps.add-item.outputs.createdItemValue + + - stepId: drop-item + operationId: remove-item + parameters: + - in: path + name: id + value: $steps.add-item.outputs.createdItemId + successCriteria: + - condition: $statusCode == 200 + + - stepId: get-after-deleting + operationId: get-by-id + parameters: + - in: path + name: id + value: $steps.add-item.outputs.createdItemId + successCriteria: + - condition: $statusCode == 404 + + - workflowId: auto-inherit + steps: + - stepId: add-item-auto-inherit + operationId: create-item + outputs: + id: $response.body#/id + - stepId: get-all-items + operationId: list-items + - stepId: get-item-by-id-auto-inherit + operationId: get-by-id + - stepId: drop-item-auto-inherit + operationId: remove-item + parameters: + - in: path + name: id + value: $steps.add-item-auto-inherit.outputs.id + successCriteria: + - condition: $statusCode == 200 diff --git a/__tests__/respect/mask-input-secrets/__snapshots__/mask-input-secrets.test.ts.snap b/__tests__/respect/mask-input-secrets/__snapshots__/mask-input-secrets.test.ts.snap new file mode 100644 index 0000000000..9e9957af49 --- /dev/null +++ b/__tests__/respect/mask-input-secrets/__snapshots__/mask-input-secrets.test.ts.snap @@ -0,0 +1,280 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should hide sensitive input values 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow mask-input-secrets.yaml / get-museum-hours + + ✓ GET /museum-hours - step get-museum-hours + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/museum-hours +    Request Headers: +      accept: application/json, application/problem+json +      authorization: ******** +      password: ******** +      username: John +      multi-word-secret: ******** + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "date": "2023-09-11", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-12", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-13", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-14", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-15", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       }, +       { +       "date": "2023-09-18", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-19", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-20", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-21", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-22", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       } +      ] + +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow mask-input-secrets.yaml / events-crud + + ✓ GET /special-events - step list-events + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      accept: application/json, application/problem+json +      authorization: ******** +      multi-word-secret: ******** + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "eventId": "f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97", +       "name": "Sasquatch Ballet", +       "location": "Seattle... probably", +       "eventDescription": "They're big, they're hairy, but they're also graceful. Come learn how the biggest feet can have the lightest touch.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 40 +       }, +       { +       "eventId": "2f14374a-9c65-4ee5-94b7-fba66d893483", +       "name": "Solar Telescope Demonstration", +       "location": "Far from the sun.", +       "eventDescription": "Look at the sun without going blind!", +       "dates": [ +       "2023-09-07", +       "2023-09-14" +       ], +       "price": 50 +       }, +       { +       "eventId": "6aaa61ba-b2aa-4868-b803-603dbbf7bfdb", +       "name": "Cook like a Caveman", +       "location": "Fire Pit on East side", +       "eventDescription": "Learn to cook on an open flame.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       }, +       { +       "eventId": "602b75e1-5696-4ab8-8c7a-f9e13580f910", +       "name": "Underwater Basket Weaving", +       "location": "Rec Center Pool next door.", +       "eventDescription": "Learn to weave baskets underwater.", +       "dates": [ +       "2023-09-12", +       "2023-09-15" +       ], +       "price": 15 +       }, +       { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Room Sea-12", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 30 +       }, +       { +       "eventId": "6744a0da-4121-49cd-8479-f8cc20526495", +       "name": "Time Traveler Tea Party", +       "location": "Temporal Tearoom", +       "eventDescription": "Sip tea with important historical figures.", +       "dates": [ +       "2023-11-18", +       "2023-11-25", +       "2023-12-02" +       ], +       "price": 60 +       }, +       { +       "eventId": "3be6453c-03eb-4357-ae5a-984a0e574a54", +       "name": "Pirate Coding Workshop", +       "location": "Computer Room", +       "eventDescription": "Captain Blackbeard shares his love of the C...language. And possibly Arrrrr (R lang).", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "9d90d29a-2af5-4206-97d9-9ea9ceadcb78", +       "name": "Llama Street Art Through the Ages", +       "location": "Auditorium", +       "eventDescription": "Llama street art?! Alpaca my bags -- let's go!", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "a3c7b2c4-b5fb-4ef7-9322-00a919864957", +       "name": "The Great Parrot Debate", +       "location": "Outdoor Amphitheatre", +       "eventDescription": "See leading parrot minds discuss important geopolitical issues.", +       "dates": [ +       "2023-11-03", +       "2023-11-10" +       ], +       "price": 35 +       }, +       { +       "eventId": "b92d46b7-4c5d-422b-87a5-287767e26f29", +       "name": "Eat a Bunch of Corn", +       "location": "Cafeteria", +       "eventDescription": "We accidentally bought too much corn. Please come eat it.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       } +      ] + +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✓ POST /special-events - step create-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      content-type: application/json +      accept: application/json, application/problem+json +      authorization: ******** +      multi-word-secret: ******** +    Request Body: +      { +       "username": "John", +       "secret": "********", +       "multiwordSecret": "********", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + + +    Response status code: 201 +    Response time: ms +    Response Body: +      { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +  Summary for mask-input-secrets.yaml +   +  Workflows: 2 passed, 2 total +  Steps: 3 passed, 3 total +  Checks: 12 passed, 12 total +  Time: ms + + +┌─────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├─────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ ✓ mask-input-secrets.yaml │ 2 │ 2 │ - │ - │ - │ +└─────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + +" +`; diff --git a/__tests__/respect/mask-input-secrets/mask-input-secrets.test.ts b/__tests__/respect/mask-input-secrets/mask-input-secrets.test.ts new file mode 100644 index 0000000000..9494e172b2 --- /dev/null +++ b/__tests__/respect/mask-input-secrets/mask-input-secrets.test.ts @@ -0,0 +1,21 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +test('should hide sensitive input values', () => { + process.env.AUTH_TOKEN = 'Basic Og=='; + + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'mask-input-secrets.yaml'); + const args = getParams(indexEntryPoint, [ + 'respect', + fixturesPath, + '--verbose', + '--input', + '{"username":"John","password":"password","secret": {"secretValue":"secretToken"}}', + ]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); + + delete process.env.AUTH_TOKEN; +}); diff --git a/__tests__/respect/mask-input-secrets/mask-input-secrets.yaml b/__tests__/respect/mask-input-secrets/mask-input-secrets.yaml new file mode 100644 index 0000000000..40f0dfcdac --- /dev/null +++ b/__tests__/respect/mask-input-secrets/mask-input-secrets.yaml @@ -0,0 +1,118 @@ +arazzo: 1.0.1 +info: + title: Mask sensitive input values + description: >- + Testing functionality of masking sensitive input values in verbose output + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + inputs: + type: object + properties: + username: + type: string + env: + type: object + properties: + AUTH_TOKEN: + type: string + format: password + password: + type: string + format: password + description: additional password. + parameters: + - in: header + name: Authorization + value: $inputs.env.AUTH_TOKEN + - in: header + name: password + value: $inputs.password + - in: header + name: username + value: $inputs.username + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + steps: + - stepId: get-museum-hours + parameters: + - in: header + name: multi-word-secret + value: Bearer {$inputs.password} + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: $sourceDescriptions.museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body + - workflowId: events-crud + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: $inputs.env.AUTH_TOKEN + - in: header + name: multi-word-secret + value: composed {$inputs.env.AUTH_TOKEN} + inputs: + type: object + properties: + username: + type: string + env: + type: object + properties: + AUTH_TOKEN: + type: string + format: password + password: + type: string + format: password + secret: + type: object + properties: + secretValue: + type: string + format: password + steps: + - stepId: list-events + description: >- + Request the list of events. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + - stepId: create-event + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + requestBody: + payload: + username: $inputs.username + secret: $inputs.password + multiwordSecret: Bearer {$inputs.secret.secretValue} + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 201 + - context: $response.body + condition: $.name == 'Mermaid Treasure Identification and Analysis' + type: jsonpath + outputs: + createdEventId: $response.body#/eventId + name: $response.body#/name diff --git a/__tests__/respect/mask-input-secrets/museum-api.yaml b/__tests__/respect/mask-input-secrets/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/mask-input-secrets/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/mask-input-secrets/museum-tickets.yaml b/__tests__/respect/mask-input-secrets/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/mask-input-secrets/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/outputs-access-syntax-variations/__snapshots__/outputs-access-syntax-variations.test.ts.snap b/__tests__/respect/outputs-access-syntax-variations/__snapshots__/outputs-access-syntax-variations.test.ts.snap new file mode 100644 index 0000000000..c0830ca2ef --- /dev/null +++ b/__tests__/respect/outputs-access-syntax-variations/__snapshots__/outputs-access-syntax-variations.test.ts.snap @@ -0,0 +1,316 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should resolve outputs access syntax variations 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow outputs-access-syntax-variations.yaml / get-museum-hours + + ✓ GET /museum-hours - step get-museum-hours + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/museum-hours +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "date": "2023-09-11", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-12", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-13", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-14", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-15", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       }, +       { +       "date": "2023-09-18", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-19", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-20", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-21", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-22", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       } +      ] + +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + Running step buy-ticket workflow $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + + ✓ POST /tickets - step buy-tickets +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow outputs-access-syntax-variations.yaml / events-crud + + ✓ GET /special-events - step list-events + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "eventId": "f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97", +       "name": "Sasquatch Ballet", +       "location": "Seattle... probably", +       "eventDescription": "They're big, they're hairy, but they're also graceful. Come learn how the biggest feet can have the lightest touch.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 40 +       }, +       { +       "eventId": "2f14374a-9c65-4ee5-94b7-fba66d893483", +       "name": "Solar Telescope Demonstration", +       "location": "Far from the sun.", +       "eventDescription": "Look at the sun without going blind!", +       "dates": [ +       "2023-09-07", +       "2023-09-14" +       ], +       "price": 50 +       }, +       { +       "eventId": "6aaa61ba-b2aa-4868-b803-603dbbf7bfdb", +       "name": "Cook like a Caveman", +       "location": "Fire Pit on East side", +       "eventDescription": "Learn to cook on an open flame.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       }, +       { +       "eventId": "602b75e1-5696-4ab8-8c7a-f9e13580f910", +       "name": "Underwater Basket Weaving", +       "location": "Rec Center Pool next door.", +       "eventDescription": "Learn to weave baskets underwater.", +       "dates": [ +       "2023-09-12", +       "2023-09-15" +       ], +       "price": 15 +       }, +       { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Room Sea-12", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 30 +       }, +       { +       "eventId": "6744a0da-4121-49cd-8479-f8cc20526495", +       "name": "Time Traveler Tea Party", +       "location": "Temporal Tearoom", +       "eventDescription": "Sip tea with important historical figures.", +       "dates": [ +       "2023-11-18", +       "2023-11-25", +       "2023-12-02" +       ], +       "price": 60 +       }, +       { +       "eventId": "3be6453c-03eb-4357-ae5a-984a0e574a54", +       "name": "Pirate Coding Workshop", +       "location": "Computer Room", +       "eventDescription": "Captain Blackbeard shares his love of the C...language. And possibly Arrrrr (R lang).", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "9d90d29a-2af5-4206-97d9-9ea9ceadcb78", +       "name": "Llama Street Art Through the Ages", +       "location": "Auditorium", +       "eventDescription": "Llama street art?! Alpaca my bags -- let's go!", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "a3c7b2c4-b5fb-4ef7-9322-00a919864957", +       "name": "The Great Parrot Debate", +       "location": "Outdoor Amphitheatre", +       "eventDescription": "See leading parrot minds discuss important geopolitical issues.", +       "dates": [ +       "2023-11-03", +       "2023-11-10" +       ], +       "price": 35 +       }, +       { +       "eventId": "b92d46b7-4c5d-422b-87a5-287767e26f29", +       "name": "Eat a Bunch of Corn", +       "location": "Cafeteria", +       "eventDescription": "We accidentally bought too much corn. Please come eat it.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       } +      ] + +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✓ POST /special-events - step create-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      content-type: application/json +      accept: application/json, application/problem+json +      authorization: Basic Og== +    Request Body: +      { +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 0 +      } + + +    Response status code: 201 +    Response time: ms +    Response Body: +      { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 0 +      } + +    ✓ success criteria check +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow outputs-access-syntax-variations.yaml / get-event-by-id + + ✓ GET /special-events/{eventId} - step get-event-by-id + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events/dad4bce8-f5cb-4078-a211-995864315e39 +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      { +       "eventId": "6744a0da-4121-49cd-8479-f8cc20526495", +       "name": "Time Traveler Tea Party", +       "location": "Temporal Tearoom", +       "eventDescription": "Sip tea with important historical figures.", +       "dates": [ +       "2023-11-18", +       "2023-11-25", +       "2023-12-02" +       ], +       "price": 60 +      } + +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +  Summary for outputs-access-syntax-variations.yaml +   +  Workflows: 3 passed, 3 total +  Steps: 5 passed, 5 total +  Checks: 22 passed, 22 total +  Time: ms + + +┌───────────────────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├───────────────────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ ✓ outputs-access-syntax-variations.yaml │ 3 │ 3 │ - │ - │ - │ +└───────────────────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + +" +`; diff --git a/__tests__/respect/outputs-access-syntax-variations/museum-api.yaml b/__tests__/respect/outputs-access-syntax-variations/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/outputs-access-syntax-variations/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/outputs-access-syntax-variations/museum-tickets.yaml b/__tests__/respect/outputs-access-syntax-variations/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/outputs-access-syntax-variations/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/outputs-access-syntax-variations/outputs-access-syntax-variations.test.ts b/__tests__/respect/outputs-access-syntax-variations/outputs-access-syntax-variations.test.ts new file mode 100644 index 0000000000..e6224557dc --- /dev/null +++ b/__tests__/respect/outputs-access-syntax-variations/outputs-access-syntax-variations.test.ts @@ -0,0 +1,15 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +test('should resolve outputs access syntax variations', () => { + process.env.AUTH_TOKEN = 'Basic Og=='; + + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'outputs-access-syntax-variations.yaml'); + const args = getParams(indexEntryPoint, ['respect', fixturesPath, '--verbose']); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); + + delete process.env.AUTH_TOKEN; +}); diff --git a/__tests__/respect/outputs-access-syntax-variations/outputs-access-syntax-variations.yaml b/__tests__/respect/outputs-access-syntax-variations/outputs-access-syntax-variations.yaml new file mode 100644 index 0000000000..1b303f23a1 --- /dev/null +++ b/__tests__/respect/outputs-access-syntax-variations/outputs-access-syntax-variations.yaml @@ -0,0 +1,100 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API + description: >- + An imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body + firstEventData: $response.body#/0/date + - stepId: buy-ticket + description: >- + Buy a ticket for the museum by calling an external workflow from another Arazzo file. + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + outputs: + ticketId: $outputs.ticketId + message: $outputs.stepFullBody#/message + outputs: + message: $steps.buy-ticket.outputs.message + - workflowId: events-crud + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: list-events + description: >- + Request the list of events. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + - stepId: create-event + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + requestBody: + payload: + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - $steps.list-events.outputs.events.0.dates.0 + - $steps.list-events.outputs.events#/0/dates/1 + price: 0 + successCriteria: + - condition: $statusCode == 201 + - condition: $workflows.get-museum-hours.outputs.message == 'Museum general entry ticket purchased' + - context: $response.body + condition: $.name == 'Mermaid Treasure Identification and Analysis' + type: jsonpath + outputs: + createdEventId: $response.body#/eventId + crearedEventData: $response.body + outputs: + createdEventData: $steps.create-event.outputs.crearedEventData + - workflowId: get-event-by-id + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-event-by-id + description: >- + Get the details of the event that was created in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/get' + parameters: + - name: eventId + in: path + value: $workflows.events-crud.outputs.createdEventData#/eventId + successCriteria: + - condition: $workflows.events-crud.outputs.createdEventData#/price == 0 + - context: $statusCode + condition: '^200$' + type: regex diff --git a/__tests__/respect/rebilly/__snapshots__/rebilly.test.ts.snap b/__tests__/respect/rebilly/__snapshots__/rebilly.test.ts.snap new file mode 100644 index 0000000000..2ee0debfe8 --- /dev/null +++ b/__tests__/respect/rebilly/__snapshots__/rebilly.test.ts.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`rebilly test case 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow rebilly.yaml / crud + + ✓ POST https://api-sandbox.rebilly.com/organizations/redocly/customers - step create-member +    ✓ success criteria check + + ✓ GET https://api-sandbox.rebilly.com/organizations/redocly/customers/{id} - step read-member +    ✓ success criteria check +    ✓ success criteria check +    ✓ success criteria check + + ✓ PUT https://api-sandbox.rebilly.com/organizations/redocly/customers/{id} - step update-member +    ✓ success criteria check +    ✓ success criteria check +    ✓ success criteria check +    ✓ success criteria check + + ✓ DELETE https://api-sandbox.rebilly.com/organizations/redocly/customers/{id} - step delete-member +    ✓ success criteria check + + ✓ GET https://api-sandbox.rebilly.com/organizations/redocly/customers/{id} - step read-non-existing-member +    ✓ success criteria check + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow rebilly.yaml / inherited + + ✓ POST /customers - step add-user +    ✓ status code check (Response code 422 matches one of description codes: [201, 401, 403, 409, 422]) +    ✓ content-type check +    ✓ schema check + + +  Summary for rebilly.yaml +   +  Workflows: 2 passed, 2 total +  Steps: 6 passed, 6 total +  Checks: 13 passed, 13 total +  Time: ms + + +┌──────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├──────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ ✓ rebilly.yaml │ 2 │ 2 │ - │ - │ - │ +└──────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + +" +`; diff --git a/__tests__/respect/rebilly/rebilly-description.yaml b/__tests__/respect/rebilly/rebilly-description.yaml new file mode 100644 index 0000000000..ee72569552 --- /dev/null +++ b/__tests__/respect/rebilly/rebilly-description.yaml @@ -0,0 +1,69513 @@ +openapi: 3.1.0 +info: + version: latest + title: All APIs + contact: + name: Rebilly API Support + url: 'https://www.rebilly.com/contact/' + email: integrations@rebilly.com + license: + name: Rebilly + url: 'https://www.rebilly.com/api-license/' + termsOfService: 'https://www.rebilly.com/terms-of-use/' + x-logo: + url: 'https://rebilly-core.redoc.ly/rb_apiLogo.svg' + backgroundColor: '#0044d4' + description: > + # Introduction + + [comment]: <> (x-product-description-placeholder) + + The Rebilly API is built on HTTP and is RESTful. + + It has predictable resource URLs and returns HTTP response codes to indicate + errors. + + It also accepts and returns JSON in the HTTP body. + + Use your favorite HTTP/REST library in your programming language when using + this API, + + or use one of the Rebilly SDKs, + + which are available in [PHP](https://github.com/Rebilly/rebilly-php) and + [JavaScript](https://github.com/Rebilly/rebilly-js-sdk). + + + Every action in the [Rebilly UI](https://app.rebilly.com) is supported by an + API which is documented and available for use, so that you may automate any + necessary workflows or processes. + + This API reference documentation contains the most commonly integrated + resources. + + + # Authentication + + + This topic describes the different forms of authentication that are + available in the Rebilly API, and how to use them. + + + Rebilly offers four forms of authentication: secret key, publishable key, + JSON Web Tokens, and public signature key. + + + - Secret API key: Use to make requests from the server side. Never share + these keys. Keep them guarded and secure. + + - Publishable API key: Use in your client-side code to tokenize payment + information. + + - JWT: Use to make short-life tokens that expire after a set period of time. + + + + + + ## Manage API keys + + + To create or manage API keys, select one of the following: + + + - Use the Rebilly UI: see [Manage API + keys](https://www.rebilly.com/docs/dev-docs/api-keys/#manage-api-keys) + + - Use the Rebilly API: see the [API key + operations](https://all-rebilly.redoc.ly/tag/API-keys). + + + For more information on API keys, see [API + keys](https://www.rebilly.com/docs/concepts-and-features/concept/api-keys). + + + # Errors + + Rebilly follows the error response format proposed in [RFC + 9457](https://tools.ietf.org/html/rfc9457), which is also known as Problem + Details for HTTP APIs. As with any API responses, your client must be + prepared to gracefully handle additional members of the response. + + + # SDKs + + + Rebilly provides a JavaScript SDK and a PHP SDK to help interact with the + Rebilly API. + + However, no SDK is required to use the API. + + + Rebilly also provides + [FramePay](https://www.rebilly.com/docs/developer-docs/framepay/), + + a client-side iFrame-based solution, to help create payment tokens while + minimizing PCI DSS compliance burdens + + and maximizing your customization ability. + + [FramePay](https://www.rebilly.com/docs/developer-docs/framepay/) interacts + with the [payment tokens creation + operation](https://all-rebilly.redoc.ly/tag/Payment-tokens/operation/PostToken). + + + ## JavaScript SDK + + + For installation and usage instructions, see + [SDKs](https://www.rebilly.com/docs/dev-docs/sdks/). + + All JavaScript SDK code examples are included in the API reference + documentation. + + + ## PHP SDK + + + For installation and usage instructions, see + [SDKs](https://www.rebilly.com/docs/dev-docs/sdks/). + + All SDK code examples are included in the API reference documentation. + + To use them, you must configure the `$client` as follows: + + + ```php + + $client = new Rebilly\Client([ + 'apiKey' => 'YourApiKeyHere', + 'baseUrl' => 'https://api.rebilly.com', + ]); + + ``` + + + # Using filter with collections + + + Rebilly provides collections filtering. Use the `?filter` parameter on + collections to define which records should be shown in the response. + + + Format description: + + + - Fields and values in the filter are separated with `:`: + `?filter=firstName:John`. + + + - Sub-fields are separated with `.`: `?filter=billingAddress.country:US`. + + + - Multiple filters are separated with `;`: + `?filter=firstName:John;lastName:Doe`. \ + They are joined with `AND` logic. Example: `firstName:John` AND `lastName:Doe`. + + - To use multiple values, use `,` as values separators: + `?filter=firstName:John,Bob`. \ + Multiple values specified for a field are joined with `OR` logic. Example: `firstName:John` OR `firstName:Bob`. + + - To negate the filter, use `!`: `?filter=firstName:!John`. + + + - To negate multiple values, use: `?filter=firstName:!John,!Bob`. + This filter rule excludes all `Johns` and `Bobs` from the response. + + - To use range filters, use: `?filter=amount:1..10`. + + + - To use a gte (greater than or equals) filter, use: `?filter=amount:1..`. + This also works for datetime-based fields. + + - To use a lte (less than or equals) filter, use: `?filter=amount:..10`. + This also works for datetime-based fields. + + - To create [specified values + lists](https://all-rebilly.redoc.ly/#tag/Lists) and use them in filters, + use: `?filter=firstName:@yourListName`. \ + You can also exclude list values: `?filter=firstName:!@yourListName`. \ + Use value lists to compare against a list of data when setting conditions for rules or binds, + or applying filters to data table segments. + Commonly used lists contain values related to conditions that target specific properties such as: customers, transactions, or BINs. + + - Datetime-based fields accept values formatted using RFC 3339. Example: + `?filter=createdTime:2021-02-14T13:30:00Z`. + + + # Expand to include embedded objects + + + Rebilly provides the ability to pre-load additional objects with a request. + + + You can use the `?expand` parameter on most requests to expand and include + embedded objects within the `_embedded` property of the response. + + The `_embedded` property contains an array of objects keyed by the expand + parameter values. + + To expand multiple objects, pass them as a comma-separated list of objects. + + + Example request containing multiple objects: + + + ``` + + ?expand=recentInvoice,customer + + ``` + + + Example response: + + + ``` + + "_embedded": [ + "recentInvoice": {...}, + "customer": {...} + ] + + ``` + + + Expand may be used on `GET`, `PATCH`, `POST`, `PUT` requests. + + + # Limit on collections offset + + + For performance reasons, take note that we have a `1000` limit on + `?offset=...`. + + For example, attempting to retrieve a collection using `?offset=1001` or + `?offset=2000` returns the same results as if you used `?offset=1000`. + + + Visit our [Data Exports API](https://all-rebilly.redoc.ly/tag/Data-exports) + for an asynchronous solution. + + + # Get started + + + The full [Rebilly API](https://all-rebilly.redoc.ly/) has over 500 + operations. + + This is likely more than you may need to implement your use cases. + + If you would like to implement a particular use case, + + [contact Rebilly](https://www.rebilly.com/support/) for guidance and + feedback on the best API operations to use for the task. + + + To integrate Rebilly, and learn about related resources and concepts, + + see [Get started](https://www.rebilly.com/docs/dev-docs/get-started/). +security: + - SecretApiKey: [] + - JWT: [] +servers: + - url: 'https://api-sandbox.rebilly.com/organizations/{organizationId}' + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). +components: + securitySchemes: + JWT: + description: >- + JWT is a short lifetime token that can be assigned a specific expiration + time. + + + Usage format: `Bearer `. + type: http + scheme: bearer + bearerFormat: JWT + ApplicationJWT: + description: >- + Applications in the Rebilly App Store can create a JSON Web Token (JWT) + by fetching an user's instance. + + For more information, see [Retrieve an application + instance](https://all-rebilly.redoc.ly/tag/Application-owners#operation/GetApplicationInstanceByOrganization). + + + Usage format: `Bearer `. + type: http + scheme: bearer + bearerFormat: JWT + PublishableApiKey: + description: >- + This authentication method is applicable to specific operations. + + + To create a publishable API key, see [Create an API + key](https://all-rebilly.redoc.ly/tag/API-keys/operation/PostApiKey). + name: Authorization + type: apiKey + in: header + SecretApiKey: + description: |- + Never share your secret keys. + Keep them guarded and secure. + + Use your secret API key only to make requests from the server side. + To authenticate, provide your secret key in the request header. + name: REB-APIKEY + type: apiKey + in: header + CustomerJWT: + description: >- + To create a JSON Web Token (JWT) using Storefront authentication, + + see [Create a session with username and + password](https://all-rebilly.redoc.ly/tag/Authentication/operation/StorefrontPostLogin). + + + Usage format: `Bearer `. + type: http + scheme: bearer + bearerFormat: JWT + parameters: + collectionLimit: + name: limit + in: query + description: Limits the number of collection items to be returned. + schema: + type: integer + minimum: 0 + maximum: 1000 + collectionOffset: + name: offset + in: query + description: >- + Specifies the starting point within the collection of items to be + returned. + schema: + type: integer + minimum: 0 + maximum: 1000 + collectionSort: + name: sort + in: query + description: |- + Sorts and orders the collection of items. To sort in descending + order, prefix with `-`. + style: form + explode: false + schema: + type: array + items: + type: string + collectionFilter: + name: filter + in: query + description: >- + Filters the collection items. This field requires + + a special format. Use `,` for multiple allowed values. Use `;` for + multiple fields. + + + For more information, see + + [Using filter with + collections](https://all-rebilly.redoc.ly/#section/Using-filter-with-collections). + schema: + type: string + collectionQuery: + name: q + in: query + description: Use this field to perform a partial search of text fields. + schema: + type: string + resourceId: + name: id + in: path + description: ID of the resource. + required: true + schema: + type: string + maxLength: 50 + pattern: '^[@~\-\.\w]+$' + collectionExpand: + name: expand + in: query + description: >- + Expands a request to include embedded objects within the `_embedded` + + property of the response. This field accepts a comma-separated list of + objects. + + + For more information, see + + [Expand to include embedded + objects](https://all-rebilly.redoc.ly/#section/Expand-to-include-embedded-objects). + schema: + type: string + collectionFields: + name: fields + in: query + description: >- + Limits the returned fields to the specified list, each field separated + by a comma. + + The ID value is always returned. + schema: + type: string + customFieldResource: + name: resource + in: path + description: Type of resource schema. + required: true + schema: + type: string + enum: + - customers + - payment-instruments + - subscriptions + - transactions + - websites + - products + - plans + - bump-offers + - gateway-accounts + externalIdentifierResource: + name: resource + in: path + description: Type of the resource. + required: true + schema: + type: string + enum: + - customers + - invoices + - invoice-items + - transactions + - journal-accounts + - journal-entries + externalIdentifierResourceId: + name: resourceId + in: path + description: ID of the resource. + required: true + schema: + type: string + maxLength: 50 + pattern: '^[@~\-\.\w]+$' + externalIdentifierService: + name: service + in: path + description: Name of the service. + required: true + schema: + type: string + enum: + - quickbooks-online + imageSize: + name: imageSize + in: query + description: >- + Resize image to specified size. + + Supports any sizes from 10x10 to 2000x2000, in the following format: + `{width}x{height}`. + + If the value is invalid, the image returns the original size. + + This parameter is ignored for non-image files. + schema: + type: string + example: 700x700 + pattern: '^[1-9]{1}[0-9]{1,3}x[1-9]{1}[0-9]{1,3}$' + applicationId: + name: applicationId + in: path + description: ID of the application. + required: true + schema: + type: string + maxLength: 50 + pattern: '^[@~\-\.\w]+$' + organizationId: + name: organizationId + in: path + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + required: true + schema: + type: string + maxLength: 50 + pattern: '^[@~\-\.\w]+$' + mediaTypeJsonPdf: + name: Accept + in: header + description: Type of response media. + schema: + type: string + enum: + - application/json + - application/pdf + default: application/json + journalRecordId: + name: journalRecordId + in: path + description: ID of the journal record. + required: true + schema: + type: string + maxLength: 50 + pattern: '^[@~\-\.\w]+$' + subscriptionExpand: + name: expand + in: query + description: >- + Expand a response to receive a full related object in the `_embedded` + path. + + + To expand multiple objects, use a comma-separated list. + + Example: `expand=recentInvoice,initialInvoice`. + + + Available arguments are: + + - `customer` + - `leadSource` + - `website` + - `shippingRate` + - `initialInvoice` + - `recentInvoice` + - `upcomingInvoice` + - `paymentInstrument` + + For more information, see [Expand to include embedded + objects](../../overview/#section/Expand-to-include-embedded-objects). + schema: + type: string + tag: + name: tag + in: path + description: Name of the tag. + required: true + schema: + type: string + pattern: '^[@~\-\.\w\s]+$' + customerId: + name: customerId + in: path + description: ID of the customer. + required: true + schema: + type: string + maxLength: 50 + pattern: '^[@~\-\.\w]+$' + kycDocumentId: + name: kycDocumentId + in: path + description: ID of the KYC document. + required: true + schema: + type: string + maxLength: 50 + pattern: '^[@~\-\.\w]+$' + credentialType: + name: type + in: path + description: Type of service credential. + required: true + schema: + type: string + enum: + - smtp + - webhook + - mailgun + - sendgrid + - aws-ses + - oauth2 + - postmark + - experian + - taxjar + - avalara + - plaid + token: + name: token + in: path + description: Token string. + required: true + schema: + type: string + systemEventType: + name: eventType + in: path + description: Type of event. + required: true + schema: + type: string + rulesVersion: + name: version + in: path + required: true + description: >- + Version of the ruleset. + + + To retrieve the full related object instead of the ID, expand the + response. + + For more information, see [Expand to include embedded + objects](#section/Expand-to-include-embedded-objects). + schema: + type: integer + minimum: 1 + integrationLabel: + name: label + in: path + description: Label of the integration. + required: true + schema: + type: string + enum: + - google-sheets + - keap-infusionsoft + - intuit-quickbooks + collectionCriteria: + name: criteria + in: query + description: Criteria parameter for requesting a collection. + schema: + type: string + storefrontCustomFieldResource: + name: resource + in: path + description: Type of resource schema. + required: true + schema: + type: string + enum: + - customers + - payment-instruments + - subscriptions + - transactions + headers: + Pagination-Total: + description: Total number of items. + schema: + type: integer + example: 332 + Pagination-Limit: + description: Maximum number of items per page. + schema: + type: integer + example: 100 + Pagination-Offset: + description: |- + Specifies the starting point within the + collection of resource results. For example, a request with + `limit=20` retrieves and displays the first 20 results on a page. A + following request with `limit=20` and `offset=20`, retrieves the next + page of 20 results. + schema: + type: integer + example: 2 + Location: + description: Location of the related resource. + schema: + type: string + format: uri + example: 'https://api.rebilly.com/example' + schemas: + CreatedTime: + type: string + description: Date and time which is set automatically when the resource is created. + format: date-time + readOnly: true + x-sortable: true + x-label: Creation Time + UpdatedTime: + type: string + description: Date and time which updates automatically when the resource is updated. + format: date-time + readOnly: true + x-sortable: true + x-label: Last Update Time + ResourceId: + type: string + description: Unique resource ID. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + SelfLink: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + Tag: + type: object + description: >- + Use tags to organize and categorize customers or KYC documents based on + keywords. + + For more information, see + [Tags](https://www.rebilly.com/docs/dev-docs/api/tag/Tags/). + required: + - name + - type + properties: + id: + description: ID of the tag. + readOnly: true + $ref: '#/components/schemas/ResourceId' + name: + description: |- + Unique name for the tag. + Tag names are not case-sensitive. + type: string + maxLength: 255 + pattern: '^[@~\-\.\w\s]+$' + example: New + type: + description: >- + Type of tag. + + Tags of a specific type can only be assigned to corresponding entity + types. + + For example, you can only use customer tags on customers. + type: string + enum: + - customer + - kyc-document + - aml-check + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + AML: + type: object + properties: + firstName: + type: string + description: 'First name of the individual, or name of entity.' + readOnly: true + example: Benjamin + lastName: + type: + - string + - 'null' + description: >- + Last name of the individual. Returns a `null` value for single-named + entities. + readOnly: true + example: Franklin + source: + type: string + readOnly: true + description: Describes which list the result is from. + sourceType: + readOnly: true + description: Describes the categories of the individual or entity. + type: array + example: + - sanctions + - enforcements + items: + type: string + enum: + - pep + - sanctions + - adverse-media + - enforcements + - state-owned-enterprise + gender: + type: + - string + - 'null' + readOnly: true + description: >- + If `type`=`individual`, this field describes the gender of the + individual. + title: + type: + - array + - 'null' + readOnly: true + description: Individual's job title. + example: + - Postmaster General + - Ambassador to France + - Ambassador to Sweden + - Supreme Executive Council of Commonwealth of Pennsylvania + items: + type: string + type: + type: string + readOnly: true + description: Describes whether the source is an individual or an entity. + enum: + - individual + - entity + legalBasis: + type: array + readOnly: true + description: 'List of references to legal documents, if they exist.' + items: + type: string + regime: + type: + - string + - 'null' + readOnly: true + description: 'Describes the government, administration, or political entity.' + example: United States Government + confidence: + type: string + description: Degree of confidence in the source list information. + readOnly: true + enum: + - weak + - medium + - strong + - very-strong + nationality: + type: string + readOnly: true + description: Nationality of the individual or entity. + address: + type: array + readOnly: true + description: Addresses associated with the individual or entity. + items: + type: object + properties: + address: + type: + - string + - 'null' + readOnly: true + description: Street address line 1. + address2: + type: + - string + - 'null' + readOnly: true + description: Street address line 2. + city: + type: + - string + - 'null' + readOnly: true + description: City. + region: + type: + - string + - 'null' + readOnly: true + description: 'State, province, or region.' + country: + type: + - string + - 'null' + readOnly: true + description: Country. + birthplace: + type: boolean + default: false + readOnly: true + description: Individual's place of birth. + dob: + type: array + readOnly: true + description: One or more possible dates of birth. + items: + type: string + readOnly: true + description: Date of birth. + format: date + example: '1706-01-17' + aliases: + type: array + readOnly: true + description: 'List of aliases, if any.' + items: + type: object + properties: + firstName: + type: string + description: First name of alias. + readOnly: true + lastName: + type: string + description: Last name of alias. + readOnly: true + authenticity: + type: string + description: Degree of confidence in the alias. + readOnly: true + enum: + - strong + - weak + - unknown + passport: + type: array + readOnly: true + description: Individual's passport information. + items: + type: object + properties: + number: + type: string + readOnly: true + description: Passport number. + registrationDate: + type: string + format: date + readOnly: true + description: Passport registration date. + comments: + type: + - string + - 'null' + readOnly: true + description: Additional information. This content varies per list. + _links: + $ref: '#/components/schemas/SelfLink' + AmlCheck: + type: object + readOnly: true + description: AML check result. + properties: + id: + type: string + description: Unique resource ID. + maxLength: 50 + example: aml_chk_0YV8XJT2ZWDR398Q8NFEM7DEPM + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + websiteId: + type: string + description: Website ID associated with the customer. + maxLength: 50 + example: web_0YV7DE4Z26DQSA1AC92FBJ7SEG + reviewerId: + type: + - string + - 'null' + maxLength: 50 + description: User ID of the person who reviewed the AML check. + example: 44433322-2c4y-483z-a0a9-158621f77a21 + reviewerName: + type: + - string + - 'null' + description: First and last name of the person who reviewed the AML check. + reviewStartTime: + type: + - string + - 'null' + format: date-time + description: Date and time when the AML check review is started. + reviewTime: + type: + - string + - 'null' + format: date-time + description: Date and time when the AML check review is completed. + priority: + type: + - string + - 'null' + default: null + description: Highest matched priority of all hits within an AML check. + source: + type: string + description: Source of the AML check. + enum: + - sign-up + - recurring + - purchase + status: + type: string + description: Status of the AML check. + readOnly: true + enum: + - pending-review + - in-review + - no-match + - confirmed-match + - false-positive + x-enumDescriptions: + pending-review: Possible AML match detected and waiting to be manually reviewed. + in-review: A manual AML match review is in progress. + no-match: No possible AML match detected. + confirmed-match: Possible AML match manually reviewed and marked as confirmed. + false-positive: Possible AML match manually reviewed and marked as false positive. + customer: + type: object + properties: + id: + description: ID of the customer. + $ref: '#/components/schemas/ResourceId' + primaryAddress: + type: object + description: Customer's data at the time of the AML check. + properties: + firstName: + type: string + maxLength: 45 + description: Customer's first name at the time of the AML check. + lastName: + type: string + maxLength: 45 + description: Customer's last name at the time of the AML check. + dob: + type: + - string + - 'null' + format: date + description: Customer's date of birth. + example: '1971-02-14' + address: + description: >- + First line of the customer's street address at the time of + the AML check. + type: + - string + - 'null' + pattern: '^[\w\s\-\/\p{L},.#;:()''&]+$' + maxLength: 60 + example: 36 Craven St + address2: + description: >- + Second line of the customer's street address at the time of + the AML check. + type: + - string + - 'null' + pattern: '^[\w\s\-\/\p{L},.#;:()''&]+$' + maxLength: 60 + city: + description: Customer's city of residence at the time of the AML check. + type: + - string + - 'null' + pattern: '^[\w\s\-\p{L},.'']+$' + maxLength: 45 + example: Austin + region: + description: Customer's region of residence at the time of the AML check. + type: + - string + - 'null' + pattern: '^[\w\s\-\/\p{L},.#;:()'']+$' + maxLength: 45 + example: Texas + country: + type: + - string + - 'null' + description: >- + Customer's country of residence at the time of the AML + check. + postalCode: + description: Customer's postal code at the time of the AML check. + type: + - string + - 'null' + pattern: '^[\w\s\-]+$' + maxLength: 10 + example: WC2N 5NF + tags: + description: List of tags that have been assigned to the customer. + type: array + items: + $ref: '#/components/schemas/Tag' + organizationId: + type: string + description: ID of the customer's organization. + maxLength: 50 + example: org_0YVDM8RC7GDADADSBSMW124JA8 + hits: + description: List of hits returned during the AML check. + type: array + items: + $ref: '#/components/schemas/AML' + tags: + description: List of AML check tags. + readOnly: true + type: array + items: + $ref: '#/components/schemas/Tag' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + BaseProblem: + type: object + additionalProperties: true + minProperties: 1 + description: >- + RFC-7807 [problem details](https://tools.ietf.org/html/rfc7807) JSON + object. + properties: + type: + type: string + description: >- + Problem type in the form of a + [URI](https://tools.ietf.org/html/rfc3986) reference. + + It should provide human-readable documentation for the problem type. + + When this member is not present, its value is assumed to be + "about:blank". + format: uri + title: + type: string + description: >- + Short, human-readable summary of the problem type. + + Other than for the purposes of localization, this should not change + from occurrence to occurrence of the problem. + detail: + type: string + description: >- + Human-readable explanation that is specific to this occurrence of + the problem. + instance: + type: string + description: >- + URI reference that identifies the specific occurrence of the + problem. + + It may or may not yield further information if dereferenced. + Unauthorized: + allOf: + - title: Unauthorized + - type: object + properties: + status: + type: integer + description: HTTP status code. + minimum: 401 + maximum: 401 + - $ref: '#/components/schemas/BaseProblem' + Forbidden: + allOf: + - title: Forbidden + - type: object + properties: + status: + type: integer + description: HTTP status code. + minimum: 403 + maximum: 403 + - $ref: '#/components/schemas/BaseProblem' + NotFound: + allOf: + - type: object + properties: + status: + type: integer + description: HTTP status code. + minimum: 404 + maximum: 404 + - $ref: '#/components/schemas/BaseProblem' + ValidationError: + allOf: + - title: Validation error + - type: object + properties: + status: + type: integer + description: HTTP status code. + minimum: 422 + maximum: 422 + - $ref: '#/components/schemas/BaseProblem' + - type: object + properties: + invalidFields: + description: Invalid field details. + type: array + items: + type: object + properties: + field: + type: string + description: |- + Name of the field. + Dot notation is used for nested object field names. + message: + description: Message field. + type: string + example: + - field: field1 + message: field1 is invalid + - field: subObject.field2 + message: field2 is invalid + - field: subObject.field2 + message: another error in the field2 + AmlCheckReview: + type: object + description: AML check review. + properties: + tag: + type: string + description: AML-related customer tag. + enum: + - aml-match-confirmed + - aml-match-false-positive + AmlConfidence: + type: + - string + - 'null' + description: Degree of confidence to assign. + enum: + - weak + - medium + - strong + - very-strong + AmlCompoundConfidence: + type: object + properties: + addressMatch: + type: object + description: "Match for the customer's city or region, or both, is found." + properties: + matchingCountry: + $ref: '#/components/schemas/AmlConfidence' + mismatchingCountry: + $ref: '#/components/schemas/AmlConfidence' + noCountry: + $ref: '#/components/schemas/AmlConfidence' + addressMismatch: + type: object + description: "Match for the customer's city or region, or both, is not found." + properties: + matchingCountry: + $ref: '#/components/schemas/AmlConfidence' + mismatchingCountry: + $ref: '#/components/schemas/AmlConfidence' + noCountry: + $ref: '#/components/schemas/AmlConfidence' + noAddress: + type: object + description: No city and no region is available to match against. + properties: + matchingCountry: + $ref: '#/components/schemas/AmlConfidence' + mismatchingCountry: + $ref: '#/components/schemas/AmlConfidence' + noCountry: + $ref: '#/components/schemas/AmlConfidence' + AmlPriority: + type: + - string + - 'null' + description: Priority level of matched AML customer information. + default: null + enum: + - p0 + - p1 + - p2 + - p3 + AmlSettings: + type: object + description: Settings for AML checking. + properties: + confidence: + type: object + description: Confidence level settings for AML matches. + properties: + dob: + type: object + description: >- + Confidence level settings for AML matches based on date of + birth. + properties: + exactMatch: + description: >- + Exact match of the customer's date of birth and name is + found. + $ref: '#/components/schemas/AmlCompoundConfidence' + inexactMatch: + description: >- + Exact match of the customer's date of birth and a fuzzy, or + inexact, name match is found. + $ref: '#/components/schemas/AmlCompoundConfidence' + noDob: + type: object + description: >- + Confidence level settings for AML matches based on a missing + date of birth. + properties: + exactMatch: + description: >- + Exact match of the customer's name is found, but a date of + birth is not. + $ref: '#/components/schemas/AmlCompoundConfidence' + inexactMatch: + description: >- + Fuzzy, or inexact match of the customer's name is found, but + a date of birth is not. + $ref: '#/components/schemas/AmlCompoundConfidence' + weakMatch: + description: >- + Weak match of the customer's name is found, but a date of + birth is not. + $ref: '#/components/schemas/AmlCompoundConfidence' + yob: + type: object + description: >- + Confidence level settings for AML matches based on year of + birth. + properties: + exactMatch: + description: >- + Exact match of the customer's year of birth and exact name + is found. + $ref: '#/components/schemas/AmlCompoundConfidence' + inexactMatch: + description: >- + Exact match of the customer's year of birth and a fuzzy, or + inexact, name match is found. + $ref: '#/components/schemas/AmlCompoundConfidence' + inexactYob: + type: object + description: >- + Confidence level settings for AML matches based on a near match + of year of birth. + properties: + exactMatch: + description: >- + Near match of the customer's year of birth and an exact name + match is found. + $ref: '#/components/schemas/AmlCompoundConfidence' + inexactMatch: + description: >- + Near match of the customer's year of birth and a fuzzy, or + inexact, name match is found. + $ref: '#/components/schemas/AmlCompoundConfidence' + priority: + type: object + description: |- + Priority level settings for AML source matches. \ + `p0` is critical priority level, and `p3` is low priority level. + properties: + pep: + type: object + description: Priorities for the `pep` source type. + properties: + veryStrong: + $ref: '#/components/schemas/AmlPriority' + strong: + $ref: '#/components/schemas/AmlPriority' + medium: + $ref: '#/components/schemas/AmlPriority' + weak: + $ref: '#/components/schemas/AmlPriority' + enforcements: + type: object + description: Priorities for the `enforcement` source type. + properties: + veryStrong: + $ref: '#/components/schemas/AmlPriority' + strong: + $ref: '#/components/schemas/AmlPriority' + medium: + $ref: '#/components/schemas/AmlPriority' + weak: + $ref: '#/components/schemas/AmlPriority' + stateOwnedEnterprise: + type: object + description: Priorities for the `state-owned-enterprise` source type. + properties: + veryStrong: + $ref: '#/components/schemas/AmlPriority' + strong: + $ref: '#/components/schemas/AmlPriority' + medium: + $ref: '#/components/schemas/AmlPriority' + weak: + $ref: '#/components/schemas/AmlPriority' + sanctions: + type: object + description: Priorities for the `sanctions` source type. + properties: + veryStrong: + $ref: '#/components/schemas/AmlPriority' + strong: + $ref: '#/components/schemas/AmlPriority' + medium: + $ref: '#/components/schemas/AmlPriority' + weak: + $ref: '#/components/schemas/AmlPriority' + adverseMedia: + type: object + description: Priorities for the `adverse-media` source type. + properties: + veryStrong: + $ref: '#/components/schemas/AmlPriority' + strong: + $ref: '#/components/schemas/AmlPriority' + medium: + $ref: '#/components/schemas/AmlPriority' + weak: + $ref: '#/components/schemas/AmlPriority' + Attachment: + type: object + required: + - fileId + - relatedId + - relatedType + properties: + id: + type: string + readOnly: true + description: ID of the attachment. + maxLength: 50 + example: att_0YV7J787J0DW0918MQQMDHWA7M + fileId: + type: string + description: ID of the linked file object. + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + relatedType: + description: Linked object type. + type: string + enum: + - customer + - customer-timeline-comment + - customer-edd-timeline-comment + - dispute + - gateway-timeline-comment + - invoice + - invoice-timeline-comment + - order-timeline-comment + - organization + - payment + - plan + - product + - subscription + - transaction + - transaction-timeline-comment + relatedId: + description: ID of the linked object. + type: string + name: + description: Name of original attachment. + type: string + description: + description: Description of the attachment. + type: string + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - file + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + file: + type: object + Conflict: + allOf: + - title: Conflict + - type: object + properties: + status: + type: integer + description: HTTP status code. + minimum: 409 + maximum: 409 + - $ref: '#/components/schemas/BaseProblem' + AuthenticationOptions: + type: object + properties: + passwordPattern: + description: Allowed password pattern. + type: + - string + - 'null' + example: '/^[0-9]+$/' + credentialTtl: + description: 'Default credential lifetime, in seconds.' + type: integer + authTokenTtl: + description: 'Default auth-token lifetime, in seconds.' + type: integer + resetTokenTtl: + description: 'Default reset-token lifetime, in seconds.' + type: integer + otpRequired: + description: >- + Specifies if a One-Time Password (OTP) is required to exchange a + token. + type: boolean + _links: + $ref: '#/components/schemas/SelfLink' + CustomerId: + type: string + description: ID of the customer resource. + maxLength: 50 + example: cus_0YV7DDSDD1C8DA64KHH2W33CPF + x-basic: true + AuthenticationTokenResponse: + type: object + title: Authentication token + required: + - username + properties: + username: + description: >- + Username of the customer who is associated with the authentication + token. + type: string + customerId: + readOnly: true + allOf: + - $ref: '#/components/schemas/CustomerId' + token: + description: ID of the authentication token. + type: string + readOnly: true + otpRequired: + description: >- + Specifies if a One-Time Password (OTP) is required to exchange the + authentication token. + type: boolean + credentialId: + description: ID of the user associated with the authentication token. + readOnly: true + $ref: '#/components/schemas/ResourceId' + expiredTime: + description: Date and time when the token expired. + type: + - string + - 'null' + format: date-time + _links: + $ref: '#/components/schemas/SelfLink' + AuthenticationTokenPasswordMode: + type: object + required: + - username + - password + properties: + username: + description: Username associated with the authentication token. + type: string + password: + description: Password associated with the authentication token. + type: string + format: password + writeOnly: true + mode: + type: string + description: >- + Specifies the authentication verification method. + + The `password` token requires the user to enter a password to log + in. + + The `passwordless` token, requires a secret API key to log in. + + To obtain an API key, see [Manage API + keys](https://www.rebilly.com/docs/dev-docs/api-keys/#manage-api-keys). + writeOnly: true + enum: + - password + customerId: + readOnly: true + allOf: + - $ref: '#/components/schemas/CustomerId' + token: + description: ID of the authentication token. + type: string + readOnly: true + otpRequired: + description: >- + Specifies if a One-Time Password (OTP) is required to exchange the + authentication token. + type: boolean + credentialId: + description: ID of the user associated with the authentication token. + readOnly: true + $ref: '#/components/schemas/ResourceId' + expiredTime: + description: Date and time when the token expired. + type: string + format: date-time + _links: + $ref: '#/components/schemas/SelfLink' + AuthenticationTokenPasswordlessMode: + type: object + required: + - customerId + properties: + customerId: + $ref: '#/components/schemas/CustomerId' + mode: + type: string + description: >- + Specifies the authentication verification method. + + The `password` token requires the user to enter a password to log + in. + + The `passwordless` token, requires a secret API key to log in. + + To obtain an API key, see [Manage API + keys](https://www.rebilly.com/docs/dev-docs/api-keys/#manage-api-keys). + writeOnly: true + enum: + - passwordless + token: + description: ID of the authentication token. + type: string + readOnly: true + otpRequired: + description: >- + Specifies if a One-Time Password (OTP) is required to exchange the + authentication token. + type: boolean + credentialId: + description: ID of the user associated with the authentication token. + readOnly: true + $ref: '#/components/schemas/ResourceId' + expiredTime: + description: Date and time when the token expired. + type: string + format: date-time + _links: + $ref: '#/components/schemas/SelfLink' + AuthenticationToken: + type: object + discriminator: + propertyName: mode + mapping: + password: '#/components/schemas/AuthenticationTokenPasswordMode' + passwordless: '#/components/schemas/AuthenticationTokenPasswordlessMode' + oneOf: + - $ref: '#/components/schemas/AuthenticationTokenPasswordMode' + - $ref: '#/components/schemas/AuthenticationTokenPasswordlessMode' + ApiKeyScope: + type: object + properties: + organizationId: + description: Array of account IDs. + type: array + items: + $ref: '#/components/schemas/ResourceId' + productId: + description: Array of product IDs. + type: array + items: + type: string + description: Unique resource ID. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + planId: + description: Array of plan IDs. + type: array + items: + type: string + description: Unique resource ID. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + customFieldName: + description: Array of custom field names. + type: array + items: + type: string + maxLength: 60 + pattern: '^[\w-]+$' + example: + organizationId: + - organizationId-id-1 + AclPermissions: + type: array + items: + type: string + format: operationId + example: + - PostFile + - StorefrontGetAccount + - StorefrontGetWebsite + - StorefrontGetKycDocumentCollection + - StorefrontGetKycDocument + - StorefrontPostKycDocument + Acl: + type: array + description: Access Control List (ACL) information. + items: + type: object + required: + - scope + - permissions + properties: + scope: + description: Scope of the API key. + $ref: '#/components/schemas/ApiKeyScope' + permissions: + description: >- + If you are creating a restricted API key, use this field to + specify individual permissions. + + Use the wildcard character `*` to provide full access. + $ref: '#/components/schemas/AclPermissions' + CustomerJWT: + type: object + properties: + id: + type: string + description: ID of the session. + maxLength: 50 + example: jwt_0YV7DEJX80CDRAKVTV478ZNJDR + readOnly: true + type: + description: Type of session. + type: string + readOnly: true + enum: + - customer + token: + description: Token used for authentication. + type: string + readOnly: true + invalidate: + description: >- + Specifies whether to invalidate a token after an exchange is + performed. + type: boolean + default: true + example: true + writeOnly: true + oneTimePassword: + description: >- + One time password that sent by email. This value must contain digits + only. + type: string + pattern: '^[0-9]{6}$' + example: '123456' + writeOnly: true + customerId: + readOnly: true + allOf: + - $ref: '#/components/schemas/CustomerId' + acl: + $ref: '#/components/schemas/Acl' + customClaims: + type: object + additionalProperties: true + example: + documents: + - identity-proof + - address-proof + redirectUrl: 'https://mywebsite.com' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + expiredTime: + description: |- + Date and time when the session expires. + The default value is one hour after the `createdTime` value. + type: string + format: date-time + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - customer + Blocklist: + type: object + required: + - type + - value + properties: + id: + type: string + description: ID of the blocklist. + readOnly: true + maxLength: 50 + example: blkl_0YV8XTSH9MD578386XQDWSEBNF + type: + description: Type of blocklist. + type: string + enum: + - payment-card + - bank-account + - customer-id + - email + - email-domain + - ip-address + - country + - fingerprint + - bin + - address + value: + description: Value of the blocklist. + type: string + expirationTime: + description: Date and time when the blocklist expires. + type: + - string + - 'null' + format: date-time + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + CouponRestrictionDiscountPerRedemption: + type: object + description: |- + Restricts the number of times that the coupon can be applied by one + redemption. For example, use this restriction to configure a coupon + that can only be applied to the first subscription renewal of a + particular product. + required: + - type + - quantity + properties: + type: + description: Type of coupon restriction. + type: string + enum: + - discounts-per-redemption + quantity: + type: integer + description: Permitted number of discounts per redemption. + CurrencyCode: + type: string + description: Currency code in ISO 4217 format. + minLength: 3 + maxLength: 3 + example: USD + x-label: Currency + x-sortable: true + CouponRestrictionMinimumOrderAmount: + type: object + description: |- + Specifies a minimum order amount for a coupon application. For + example, if the restriction amount is $20, the coupon is only + applied to invoices with a total amount of $20 or more. + required: + - type + - amount + - currency + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - minimum-order-amount + amount: + type: integer + description: Minimum order quantity that is required to apply the coupon. + currency: + $ref: '#/components/schemas/CurrencyCode' + CouponRestrictionPaidByTime: + type: object + description: >- + Specifies a date and time at which a coupon redemption expires if the + invoice is not paid. + required: + - type + - time + properties: + type: + description: Type of coupon restriction. + type: string + enum: + - paid-by-time + time: + type: string + format: date-time + description: >- + Date and time when a coupon redemption is no longer valid, and the + coupon is removed from unpaid invoices if applied. + + + > **Note:** This date-time cannot be changed. + CouponRestrictionRestrictToInvoices: + type: object + description: Restricts a coupon to specific invoices. + required: + - type + - invoiceIds + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - restrict-to-invoices + invoiceIds: + type: array + description: Invoice IDs on which a coupon can be applied. + items: + type: string + CouponRestrictionRestrictToPlans: + type: object + description: Restricts a coupon to specific pricing plans. + required: + - type + - planIds + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - restrict-to-plans + planIds: + type: array + description: Plan IDs on which a coupon can be applied. + items: + type: string + minimumQuantity: + type: integer + default: 1 + description: >- + Minimum quantity that is required to apply the restriction and + subsequent discount. + CouponRestrictionRestrictToProducts: + type: object + description: Restricts a coupon to specific products. + required: + - type + - productIds + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - restrict-to-products + productIds: + type: array + description: Product IDs on which a coupon can be applied. + items: + type: string + minimumQuantity: + type: integer + default: 1 + description: >- + Minimum quantity that is required to apply the restriction and + subsequent discount. + CouponRestrictionRestrictToSubscriptions: + type: object + description: Restricts a coupon to specific order subscriptions. + required: + - type + - subscriptionIds + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - restrict-to-subscriptions + subscriptionIds: + type: array + description: Subscription IDs on which a coupon can be applied. + items: + type: string + RedemptionRestriction: + description: Redemption restrictions. + type: object + discriminator: + propertyName: type + mapping: + discounts-per-redemption: '#/components/schemas/CouponRestrictionDiscountPerRedemption' + minimum-order-amount: '#/components/schemas/CouponRestrictionMinimumOrderAmount' + paid-by-time: '#/components/schemas/CouponRestrictionPaidByTime' + restrict-to-invoices: '#/components/schemas/CouponRestrictionRestrictToInvoices' + restrict-to-plans: '#/components/schemas/CouponRestrictionRestrictToPlans' + restrict-to-products: '#/components/schemas/CouponRestrictionRestrictToProducts' + restrict-to-subscriptions: '#/components/schemas/CouponRestrictionRestrictToSubscriptions' + anyOf: + - $ref: '#/components/schemas/CouponRestrictionDiscountPerRedemption' + - $ref: '#/components/schemas/CouponRestrictionMinimumOrderAmount' + - $ref: '#/components/schemas/CouponRestrictionPaidByTime' + - $ref: '#/components/schemas/CouponRestrictionRestrictToInvoices' + - $ref: '#/components/schemas/CouponRestrictionRestrictToPlans' + - $ref: '#/components/schemas/CouponRestrictionRestrictToProducts' + - $ref: '#/components/schemas/CouponRestrictionRestrictToSubscriptions' + CouponRedemption: + type: object + description: Coupon redemption log. + properties: + id: + type: string + readOnly: true + description: Unique resource ID. + maxLength: 50 + example: cpn_rdm_0YVCNKF81GD778N4YNVGDJK558 + couponId: + type: string + description: ID of the coupon. + maxLength: 50 + example: cpn_0YVCNKF81GD778N4YNVGDJK558 + customerId: + $ref: '#/components/schemas/CustomerId' + additionalRestrictions: + description: Additional coupon redemption restrictions. + type: array + items: + $ref: '#/components/schemas/RedemptionRestriction' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + canceledTime: + description: Date and time when the coupon is canceled. + readOnly: true + x-sortable: true + type: + - string + - 'null' + format: date-time + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + - coupon + DiscountContext: + type: string + description: Context in which the discount applies. + default: items + x-label: Context + x-sortable: true + x-basic: true + x-enumDescriptions: + items: items (less discounts) + shipping: shipping (less discounts) + items-and-shipping: items and shipping (less discounts) + enum: + - items + - shipping + - items-and-shipping + DiscountFixed: + type: object + description: Coupon fixed amount discount. + required: + - amount + - currency + - type + properties: + amount: + description: Amount of discount. + type: number + format: double + minimum: 0.01 + currency: + $ref: '#/components/schemas/CurrencyCode' + type: + type: string + enum: + - fixed + x-label: Type + x-sortable: true + x-basic: true + context: + $ref: '#/components/schemas/DiscountContext' + DiscountPercent: + type: object + description: Coupon percent discount. + required: + - value + - type + properties: + value: + description: Percentage of discount. + type: number + format: double + minimum: 0.01 + maximum: 100 + type: + type: string + enum: + - percent + x-label: Type + x-sortable: true + x-basic: true + context: + $ref: '#/components/schemas/DiscountContext' + Discount: + type: object + description: Coupon discount. + discriminator: + propertyName: type + mapping: + fixed: '#/components/schemas/DiscountFixed' + percent: '#/components/schemas/DiscountPercent' + oneOf: + - $ref: '#/components/schemas/DiscountFixed' + - $ref: '#/components/schemas/DiscountPercent' + CouponRestrictionRedemptionsPerCustomer: + type: object + description: |- + Restricts the number of times that a coupon can be redeemed by one + customer. For example, use this restriction to limit the number of + redemptions to one per customer. + required: + - type + - quantity + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - redemptions-per-customer + quantity: + type: integer + description: Permitted number of redemptions per customer. + CouponRestrictionRestrictToBxgy: + type: object + description: >- + Provides "buy X get Y" promotions. + + These promotions incentivize new customers to buy and also reward + existing customers. + + This restriction type enables you to add discounted plans to an invoice + when a certain quantity of other plans are purchased. + + For example, if a customer buys two or more books, they get one free + bookmark. + required: + - type + - buy + - get + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - restrict-to-bxgy + buy: + type: array + description: >- + One or more plan and quantity definitions that must be purchased to + receive all plans and quantities that are defined in the `get` + instruction. + minItems: 1 + items: + type: object + required: + - planId + - quantity + properties: + planId: + type: string + description: Plan ID of the item being purchased. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + quantity: + type: integer + minimum: 1 + description: >- + Total quantity of the associated plan ID that must be + purchased for this restriction to apply. + get: + type: array + description: >- + One or more plan and quantity definitions that are added to a + purchase if all definitions in the `buy` condition are met. + minItems: 1 + items: + type: object + required: + - planId + - quantity + properties: + planId: + type: string + description: Plan ID of the item that is to be received by the customer. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + quantity: + type: integer + minimum: 1 + description: >- + Total quantity of the associated plan ID that is to be + received by the customer. + CouponRestrictionRestrictToCountries: + type: object + description: Restricts a coupon for use in specific countries. + required: + - type + - countries + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - restrict-to-countries + countries: + type: array + description: >- + Countries where the coupon can be applied, in ISO Alpha-2 code + format. + + For examples, see + [ISO.org](https://www.iso.org/obp/ui/#search/code/). + items: + type: string + pattern: '^[A-Z]{2}$' + CouponRestrictionRestrictToCustomerTags: + type: object + description: Restricts a coupon to customers with specific tags. + required: + - type + - tags + - requireAllTags + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - restrict-to-customer-tags + tags: + type: array + description: Customer tags on which a coupon can be applied. + items: + type: string + requireAllTags: + type: boolean + description: Determines the behavior for checking customer tags. + enum: + - true + - false + x-enumDescriptions: + 'true': Customer must have all listed tags to redeem a coupon. + 'false': >- + Customer must have at least one of the listed tags to redeem a + coupon. + CouponRestrictionRestrictToCustomers: + type: object + description: Restricts a coupon to specific customers. + required: + - type + - customerIds + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - restrict-to-customers + customerIds: + type: array + description: Customer IDs on which a coupon can be applied. + items: + type: string + CouponRestrictionExclusiveApplication: + type: object + description: >- + Restricts a coupon so that it cannot be used in combination with other + coupons. + + If more than one coupon is active, a coupon with this restriction is + only applied if it provides a larger discount than the other coupons + combined. + + If a coupon with this restriction is applied, all other coupons are + removed. + required: + - type + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - restrict-to-exclusive-application + CouponRestrictionTotalRedemptions: + type: object + description: >- + Restricts the number of times a coupon can be redeemed by different + customers. + required: + - type + - quantity + properties: + type: + type: string + description: Type of coupon restriction. + enum: + - total-redemptions + quantity: + type: integer + description: Permitted total number of redemptions. + CouponRestriction: + description: Coupon restrictions. + type: object + discriminator: + propertyName: type + mapping: + discounts-per-redemption: '#/components/schemas/CouponRestrictionDiscountPerRedemption' + minimum-order-amount: '#/components/schemas/CouponRestrictionMinimumOrderAmount' + paid-by-time: '#/components/schemas/CouponRestrictionPaidByTime' + redemptions-per-customer: '#/components/schemas/CouponRestrictionRedemptionsPerCustomer' + restrict-to-bxgy: '#/components/schemas/CouponRestrictionRestrictToBxgy' + restrict-to-countries: '#/components/schemas/CouponRestrictionRestrictToCountries' + restrict-to-customer-tags: '#/components/schemas/CouponRestrictionRestrictToCustomerTags' + restrict-to-customers: '#/components/schemas/CouponRestrictionRestrictToCustomers' + restrict-to-exclusive-application: '#/components/schemas/CouponRestrictionExclusiveApplication' + restrict-to-invoices: '#/components/schemas/CouponRestrictionRestrictToInvoices' + restrict-to-plans: '#/components/schemas/CouponRestrictionRestrictToPlans' + restrict-to-products: '#/components/schemas/CouponRestrictionRestrictToProducts' + restrict-to-subscriptions: '#/components/schemas/CouponRestrictionRestrictToSubscriptions' + total-redemptions: '#/components/schemas/CouponRestrictionTotalRedemptions' + anyOf: + - $ref: '#/components/schemas/CouponRestrictionDiscountPerRedemption' + - $ref: '#/components/schemas/CouponRestrictionExclusiveApplication' + - $ref: '#/components/schemas/CouponRestrictionMinimumOrderAmount' + - $ref: '#/components/schemas/CouponRestrictionPaidByTime' + - $ref: '#/components/schemas/CouponRestrictionRedemptionsPerCustomer' + - $ref: '#/components/schemas/CouponRestrictionRestrictToBxgy' + - $ref: '#/components/schemas/CouponRestrictionRestrictToCountries' + - $ref: '#/components/schemas/CouponRestrictionRestrictToCustomerTags' + - $ref: '#/components/schemas/CouponRestrictionRestrictToCustomers' + - $ref: '#/components/schemas/CouponRestrictionRestrictToInvoices' + - $ref: '#/components/schemas/CouponRestrictionRestrictToPlans' + - $ref: '#/components/schemas/CouponRestrictionRestrictToProducts' + - $ref: '#/components/schemas/CouponRestrictionRestrictToSubscriptions' + - $ref: '#/components/schemas/CouponRestrictionTotalRedemptions' + Coupon: + type: object + description: Coupons and discounts. + required: + - discount + - issuedTime + properties: + id: + type: string + description: >- + ID of the coupon. This value is also known as the coupon redemption + code. + readOnly: true + maxLength: 50 + example: cpn_0YVCNKF81GD778N4YNVGDJK558 + discount: + $ref: '#/components/schemas/Discount' + restrictions: + description: Coupon restrictions. + type: array + items: + $ref: '#/components/schemas/CouponRestriction' + redemptionsCount: + type: integer + description: Total number of times that a coupon is redeemed. + minimum: 0 + readOnly: true + status: + type: string + description: Specifies the current status of the coupon. + readOnly: true + enum: + - draft + - issued + - expired + description: + type: string + x-basic: true + description: >- + Description of the coupon. Use this field to provide an invoice + discount item description. + + + If this field is empty, the invoice discount item description uses + the related coupon ID, in the following format: + + `Coupon "COUPON-ID"`. + issuedTime: + description: |- + Date and time when the coupon is issued. + This value may also be described as start time of the coupon. + type: string + x-label: Valid from + x-sortable: true + x-basic: true + format: date-time + expiredTime: + description: Date and time when the coupon expires. + type: + - string + - 'null' + x-label: Valid until + x-sortable: true + x-basic: true + format: date-time + revision: + description: |- + Number of times the coupon data has been modified. + + Use the revision number when analyzing webhook data to + determine if a change should take precedence over the current + representation. + type: integer + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + CouponExpiration: + type: object + required: + - expiredTime + properties: + expiredTime: + description: >- + Date and time when the coupon expires. + + This value must be greater than the `issuedTime` value. + + If this field contains a null or empty string, the coupon expires + immediately. + type: string + format: date-time + BlankProblem: + description: >- + Problem Details JSON Object + [[RFC7807](https://tools.ietf.org/html/rfc7807)]. + type: object + minProperties: 1 + additionalProperties: true + properties: + status: + type: integer + description: HTTP status code. + minimum: 400 + maximum: 599 + title: + type: string + description: >- + Short, human-readable summary of the problem type. + + It SHOULD NOT change from occurrence to occurrence of the problem, + except for purposes of localization. + DocumentedProblem: + type: object + allOf: + - properties: + type: + type: string + description: >- + URI reference [[RFC3986](https://tools.ietf.org/html/rfc3986)] + that identifies the problem type. + + It should provide human-readable documentation for the problem + type. + + When this member is not present, its value is assumed to be + "about:blank". + format: uri + example: 'https://www.rebilly.com/docs/' + - $ref: '#/components/schemas/BlankProblem' + DetailedProblem: + type: object + allOf: + - $ref: '#/components/schemas/BlankProblem' + - properties: + detail: + type: string + description: >- + Human-readable explanation specific to this occurrence of the + problem. + Problem: + type: object + allOf: + - $ref: '#/components/schemas/DocumentedProblem' + - $ref: '#/components/schemas/DetailedProblem' + Error: + type: object + allOf: + - $ref: '#/components/schemas/Problem' + - properties: + error: + deprecated: true + type: string + CustomerCredential: + type: object + required: + - username + - password + - customerId + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 50 + example: cus_crd_0YV9Y706QGCB39FQD380G1ZHZH + username: + description: Username associated with the credential. + type: string + password: + description: Password associated with the credential. + type: string + format: password + writeOnly: true + customerId: + $ref: '#/components/schemas/CustomerId' + expiredTime: + description: Date and time when the credential expires. + type: + - string + - 'null' + format: date-time + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + ArrayCustomField: + type: object + required: + - type + properties: + name: + description: Name of the custom field. + type: string + readOnly: true + type: + description: >- + Describes the data type. Custom field type value cannot be changed. + + + Array of strings with up to 255 characters. Maximum size is 1000 + elements. + type: string + enum: + - array + description: + description: Description of the custom field. + type: + - string + - 'null' + additionalSchema: + type: + - object + - 'null' + description: Additional schema which adds additional values constrains. + properties: + allowedValues: + description: List of allowed values. + type: array + items: + type: string + required: + description: >- + Specifies whether this value is required. + + This value can be combined with any parameter and is available + for custom fields of any type. + type: boolean + default: + description: Default value. + type: array + items: + type: string + _links: + $ref: '#/components/schemas/SelfLink' + BooleanCustomField: + type: object + required: + - type + properties: + name: + description: Name of the custom field. + type: string + readOnly: true + type: + description: |- + Describes the data type. Custom field type value cannot be changed. + + Specifies a `true` or `false` value. + type: string + enum: + - boolean + description: + description: Description of the custom field. + type: + - string + - 'null' + additionalSchema: + type: + - object + - 'null' + description: Additional schema which adds additional values constrains. + properties: + required: + description: Specifies whether this value is required. + type: boolean + default: + description: Default value. + type: boolean + _links: + $ref: '#/components/schemas/SelfLink' + DateCustomField: + type: object + required: + - type + properties: + name: + description: Name of the custom field. + type: string + readOnly: true + type: + description: >- + Describes the data type. Custom field type value cannot be changed. + + + String in [full-date](https://www.rfc-editor.org/rfc/rfc3339) format + (YYYY-MM-DD). + type: string + enum: + - date + description: + description: Description of the custom field. + type: + - string + - 'null' + additionalSchema: + type: + - object + - 'null' + description: Additional schema which adds additional values constrains. + properties: + required: + description: Specifies whether this value is required. + type: boolean + default: + description: Default value. + type: string + format: date + _links: + $ref: '#/components/schemas/SelfLink' + DateTimeCustomField: + type: object + required: + - type + properties: + name: + description: Name of the custom field. + type: string + readOnly: true + type: + description: >- + Describes the data type. Custom field type value cannot be changed. + + + String in [date-time](https://www.rfc-editor.org/rfc/rfc3339) format + (YYYY-MM-DDTHH:MM:SSZ). + type: string + enum: + - datetime + description: + description: Description of the custom field. + type: + - string + - 'null' + additionalSchema: + type: + - object + - 'null' + description: Additional schema which adds additional values constrains. + properties: + required: + description: Specifies whether this value is required. + type: boolean + default: + description: Default value. + type: string + format: date-time + _links: + $ref: '#/components/schemas/SelfLink' + IntegerCustomField: + type: object + required: + - type + properties: + name: + description: Name of the custom field. + type: string + readOnly: true + type: + description: |- + Describes the data type. Custom field type value cannot be changed. + + Cardinal value of between `-2^31` and `2^31-1`. + type: string + enum: + - integer + description: + description: Description of the custom field. + type: + - string + - 'null' + additionalSchema: + type: + - object + - 'null' + description: Additional schema which adds additional values constrains. + properties: + minimum: + description: |- + Minimum allowed integer. + This value can be combined with the `maximum` parameter. + type: integer + maximum: + description: |- + Maximum allowed integer. + This value can be combined with the `minimum` parameter. + type: integer + required: + description: Specifies whether this value is required. + type: boolean + default: + description: Default value. + type: integer + _links: + $ref: '#/components/schemas/SelfLink' + NumberCustomField: + type: object + required: + - type + properties: + name: + description: Name of the custom field. + type: string + readOnly: true + type: + description: >- + Describes the data type. Custom field type value cannot be changed. + + + Float value. This field also accepts cardinal values which are + interpreted as float. + type: string + enum: + - number + description: + description: Description of the custom field. + type: + - string + - 'null' + additionalSchema: + type: + - object + - 'null' + description: Additional schema which adds additional values constrains. + properties: + minimum: + description: |- + Minimum allowed number. + This value can be combined with the `maximum` parameter. + type: number + format: double + maximum: + description: |- + Maximum allowed number. + This value can be combined with the `minimum` parameter. + type: number + format: double + required: + description: Specifies whether this value is required. + type: boolean + default: + description: Default value. + type: number + format: double + _links: + $ref: '#/components/schemas/SelfLink' + StringCustomField: + type: object + required: + - type + properties: + name: + description: Name of the custom field. + type: string + readOnly: true + type: + description: |- + Describes the data type. Custom field type value cannot be changed. + + Regular string of up to 255 characters. + type: string + enum: + - string + description: + description: Description of the custom field. + type: + - string + - 'null' + additionalSchema: + type: + - object + - 'null' + description: Additional schema which adds additional values constrains. + properties: + allowedValues: + description: List of allowed values. + type: array + items: + type: string + maxLength: + description: |- + Maximum allowed string length . + A maximum length of 4000 characters can be configured. + By default, this value is set to 255. + type: integer + pattern: + description: Allowed regular expression of a string. + type: string + example: '/^[0-9]+$/' + required: + description: Specifies whether this value is required. + type: boolean + default: + description: Default value. + type: string + _links: + $ref: '#/components/schemas/SelfLink' + MonetaryCustomField: + type: object + required: + - type + properties: + name: + description: Name of the custom field. + type: string + readOnly: true + type: + description: |- + Describes the data type. Custom field type value cannot be changed. + + A map of 3-letter currency, code, and amount. + Example: `{"currency": "EUR", "amount": 25.30}`. + type: string + enum: + - monetary + description: + description: Description of the custom field. + type: + - string + - 'null' + additionalSchema: + type: + - object + - 'null' + description: Additional schema which adds additional values constrains. + properties: + required: + description: Specifies whether this value is required. + type: boolean + default: + description: Default value. + type: object + properties: + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + type: number + format: double + _links: + $ref: '#/components/schemas/SelfLink' + CustomField: + description: Separate custom field schema. + type: object + discriminator: + propertyName: type + mapping: + array: '#/components/schemas/ArrayCustomField' + boolean: '#/components/schemas/BooleanCustomField' + date: '#/components/schemas/DateCustomField' + datetime: '#/components/schemas/DateTimeCustomField' + integer: '#/components/schemas/IntegerCustomField' + number: '#/components/schemas/NumberCustomField' + string: '#/components/schemas/StringCustomField' + monetary: '#/components/schemas/MonetaryCustomField' + oneOf: + - $ref: '#/components/schemas/ArrayCustomField' + - $ref: '#/components/schemas/BooleanCustomField' + - $ref: '#/components/schemas/DateCustomField' + - $ref: '#/components/schemas/DateTimeCustomField' + - $ref: '#/components/schemas/IntegerCustomField' + - $ref: '#/components/schemas/NumberCustomField' + - $ref: '#/components/schemas/StringCustomField' + - $ref: '#/components/schemas/MonetaryCustomField' + WebsiteId: + type: string + description: >- + ID of the website. + + A website is where an organization obtains a customer. + + For more information, see [Obtain an organization ID and website + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + maxLength: 50 + example: web_0YV7DE4Z26DQSA1AC92FBJ7SEG + PaymentMethod: + type: string + description: Payment method. + enum: + - payment-card + - ach + - cash + - check + - paypal + - AdvCash + - Aircash + - Airpay + - Alfa-click + - Alipay + - AmazonPay + - Apple Pay + - AstroPay Card + - AstroPay-GO + - BankSEND + - BankReferenced + - bank-transfer + - bank-transfer-2 + - bank-transfer-3 + - bank-transfer-4 + - bank-transfer-5 + - bank-transfer-6 + - bank-transfer-7 + - bank-transfer-8 + - bank-transfer-9 + - Baloto + - Beeline + - Belfius-direct-net + - bitcoin + - Bizum + - Blik + - Boleto + - Boleto-2 + - Boleto-3 + - cash-deposit + - CASHlib + - CashToCode + - CCAvenue + - China UnionPay + - Cleo + - CODVoucher + - Conekta-oxxo + - Conekta-spei + - cryptocurrency + - Cupon-de-pagos + - CyberSource + - Dimoco-pay-smart + - Directa24Card + - domestic-cards + - Efecty + - echeck + - ecoPayz + - ecoVoucher + - EPS + - ePay.bg + - Ethereum + - e-wallet + - ezyEFT + - eZeeWallet + - FasterPay + - Flexepin + - Giropay + - Google Pay + - Gpaysafe + - iDebit + - iDEAL + - ING-homepay + - INOVAPAY-pin + - INOVAPAY-wallet + - InstaDebit + - InstantPayments + - instant-bank-transfer + - Interac-online + - Interac-eTransfer + - Interac-express-connect + - Interac + - invoice + - iWallet + - Jeton + - jpay + - KakaoPay + - Khelocard + - Klarna + - KNOT + - Litecoin + - loonie + - LPG-online + - LPG-payment-card + - Matrix + - MaxiCash + - Megafon + - MercadoPago + - MiFinity-eWallet + - miscellaneous + - MobilePay + - Multibanco + - Bancontact + - Bancontact-mobile + - MTS + - MuchBetter + - MyFatoorah + - Neosurf + - Netbanking + - Neteller + - Nordea-Solo + - NordikCoin + - OchaPay + - online-bank-transfer + - Onlineueberweisen + - oriental-wallet + - OXXO + - P24 + - Pagadito + - PagoEffectivo + - Pagsmile-lottery + - Pagsmile-deposit-express + - PayCash + - Payco + - Payeer + - PaymentAsia-crypto + - Paysafecard + - PayTabs + - Pay4Fun + - Paynote + - Paymero + - Paymero-QR + - PayU + - PayULatam + - Perfect-money + - Piastrix + - PIX + - PinPay + - phone + - PhonePe + - POLi + - PostFinance-card + - PostFinance-e-finance + - QIWI + - QPay + - QQPay + - rapyd-checkout + - rebilly-hosted-payment-form + - Resurs + - SafetyPay + - Samsung Pay + - SEPA + - Siirto + - Skrill + - Skrill Rapid Transfer + - SMSVoucher + - Sofort + - SparkPay + - swift-dbt + - Tele2 + - Telr + - Terminaly-RF + - Tether + - ToditoCash-card + - Trustly + - Tupay + - TWINT + - UniCrypt + - UPayCard + - UPI + - USD-coin + - VCreditos + - VegaWallet + - VenusPoint + - voucher + - voucher-2 + - voucher-3 + - voucher-4 + - Wallet88 + - Webmoney + - Webpay + - Webpay-2 + - Webpay Card + - WeChat Pay + - XPay-P2P + - XPay-QR + - Yandex-money + - Zotapay + - Zimpler + VaultedInstrument: + description: Vaulted payment instrument. + title: Vaulted instrument + required: + - method + - paymentInstrumentId + properties: + method: + description: >- + Payment method supported vault. + + For more information, see [Payment + instrument](https://all-rebilly.redoc.ly/tag/Payment-instruments). + type: string + enum: + - payment-card + - ach + - paypal + allOf: + - $ref: '#/components/schemas/PaymentMethod' + paymentInstrumentId: + type: string + description: ID of the payment instrument. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + AlternativePaymentInstrument: + description: Alternative payment method instrument. + title: Alternative instrument + type: object + required: + - method + properties: + method: + $ref: '#/components/schemas/PaymentMethod' + paymentInstrumentId: + type: string + description: ID of the payment instrument. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + CashInstrument: + description: Cash payment instrument object. + title: Cash + type: object + required: + - method + properties: + method: + type: string + enum: + - cash + receivedBy: + description: Individual or entity that received the payment. + type: string + CheckInstrument: + description: Check payment instrument object. + title: Check + type: object + required: + - method + properties: + method: + type: string + enum: + - check + reference: + description: Reference data. + type: string + ResourceCustomFields: + description: >- + Use custom fields to extend a resource scheme to include custom data + that is not provided as a common field. + + For more information, see [Custom + fields](https://all-rebilly.redoc.ly/tag/Custom-fields). + type: object + default: {} + example: + foo: bar + ContactPhoneNumbers: + description: List of phone numbers associated with the contact. + type: array + items: + type: object + required: + - label + - value + properties: + label: + description: Phone number label or name. + type: string + maxLength: 45 + example: main + value: + description: Phone number value. + type: string + maxLength: 50 + example: 512-710-1640 + primary: + description: >- + Specifies if the phone number is the contact's primary phone + number. + type: boolean + example: true + ContactEmails: + description: List of email addresses associated with the contact. + type: array + items: + type: object + required: + - label + - value + properties: + label: + description: Email label or name. + type: string + maxLength: 45 + example: main + value: + description: Email address value. + type: string + format: email + maxLength: 255 + example: rebilly@example.com + primary: + description: >- + Specifies if the email address is the contact's primary email + address. + type: boolean + example: true + ContactObject: + type: object + description: Contact's information. + properties: + firstName: + description: Contact's first name. + type: + - string + - 'null' + pattern: '^[\w\s\-\p{L},.'']+$' + maxLength: 45 + example: Benjamin + lastName: + description: Contact's last name. + type: + - string + - 'null' + pattern: '^[\w\s\-\p{L},.'']+$' + maxLength: 45 + example: Franklin + organization: + description: Contact's organization. + type: + - string + - 'null' + pattern: '^[\w\s\-\p{L},.''&]+$' + maxLength: 255 + example: Rebilly + address: + description: First line of the contact's street address. + type: + - string + - 'null' + pattern: '^[\w\s\-\/\p{L},.#;:()''&]+$' + maxLength: 60 + example: 36 Craven St + address2: + description: Second line of the contact's street address. + type: + - string + - 'null' + pattern: '^[\w\s\-\/\p{L},.#;:()''&]+$' + maxLength: 60 + city: + description: Contact's city of residence. + type: + - string + - 'null' + pattern: '^[\w\s\-\p{L},.'']+$' + maxLength: 45 + example: Austin + region: + description: Contact's region of residence. + type: + - string + - 'null' + pattern: '^[\w\s\-\/\p{L},.#;:()'']+$' + maxLength: 45 + example: Texas + country: + description: >- + Contact's country of residence in ISO 3166 alpha-2 country code. + + For examples, see + [ISO.org](https://www.iso.org/obp/ui/#search/code/). + type: + - string + - 'null' + pattern: '^[A-Z]{2}$' + maxLength: 2 + example: GB + postalCode: + description: Contact's postal code. + type: + - string + - 'null' + pattern: '^[\w\s\-]+$' + maxLength: 10 + example: WC2N 5NF + phoneNumbers: + $ref: '#/components/schemas/ContactPhoneNumbers' + emails: + $ref: '#/components/schemas/ContactEmails' + dob: + description: Contact's date of birth in ISO-8601 `YYYY-MM-DD` format. + type: + - string + - 'null' + format: date + example: '1980-04-01' + jobTitle: + description: Contact's job title. + type: + - string + - 'null' + pattern: '^[\w\s\-\/\p{L},.#;:()'']+$' + maxLength: 255 + example: CEO + hash: + description: Use this value to compare contacts for identical attribute values. + type: string + maxLength: 40 + example: 056ae6d97c788b9e98b049ebafd7b229bf852221 + readOnly: true + Company: + type: + - object + - 'null' + description: >- + Company information that is associated with the customer's primary email + address domain. + + + This is a paid feature, to enable it [contact + Rebilly](https://www.rebilly.com/support/). + readOnly: true + properties: + name: + type: + - string + - 'null' + description: Name of the company. + domain: + type: string + description: Website domain of the company. + yearFounded: + type: + - number + - 'null' + description: Founding year of the company. + format: integer + industry: + type: + - string + - 'null' + description: Industry the company is associated with. + employeesCount: + type: + - number + - 'null' + description: Number of employees in the company. + format: integer + country: + type: + - string + - 'null' + description: Country where the company is based. + locality: + type: + - string + - 'null' + description: Locality or region where the company is based. + _links: + type: array + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - linkedin + CustomerAverageValue: + type: object + readOnly: true + description: Average customer value. + properties: + currency: + description: Merchant's reporting currency. + $ref: '#/components/schemas/CurrencyCode' + amount: + x-type: Money + x-label: Average Value + x-sortable: true + x-basic: true + description: Average approved payment amount in merchant's reporting currency. + type: number + format: double + amountUsd: + description: Average approved payment amount in USD. + type: number + format: double + CustomerLifetimeRevenue: + type: object + readOnly: true + description: Customer's lifetime revenue. + properties: + currency: + description: Merchant's reporting currency. + $ref: '#/components/schemas/CurrencyCode' + amount: + x-type: Money + x-label: Lifetime Revenue + x-sortable: true + x-basic: true + description: Revenue amount in the merchant's reporting currency. + type: number + format: double + amountUsd: + description: Revenue amount in USD. + type: number + format: double + OrganizationId: + type: string + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + maxLength: 50 + example: org_0YVDM8RC7GDADADSBSMW124JA8 + TaxNumber: + type: object + required: + - type + - value + properties: + type: + type: string + description: Type of the tax number. + enum: + - eu-vat + - other + example: eu-vat + value: + type: string + description: Value of the tax number. + example: GB980780684 + isDefault: + type: boolean + description: >- + Determines if the tax number is selected as default to display on + invoices. + example: true + isValid: + type: + - boolean + - 'null' + description: >- + Determines if the tax number passed the EU official [VIES + validation](https://ec.europa.eu/taxation_customs/vies/#/vat-validation). + example: true + readOnly: true + Customer: + type: object + description: Customer information. + properties: + id: + type: string + description: ID of the customer. + readOnly: true + example: cus_0YV7DDSDD1C8DA64KHH2W33CPF + maxLength: 50 + email: + description: Customer's email address. + type: + - string + - 'null' + format: email + readOnly: true + x-sortable: true + x-basic: true + firstName: + description: Customer's first name. + type: + - string + - 'null' + x-basic: true + readOnly: true + lastName: + description: Customer's last name. + type: + - string + - 'null' + x-sortable: true + x-basic: true + readOnly: true + websiteId: + x-sortable: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + paymentToken: + type: string + writeOnly: true + description: >- + Write-only payment token. + + If supplied, the token is converted into a payment instrument and + set as the `defaultPaymentInstrument` value. + + If both are supplied, the value of this property overrides the + `defaultPaymentInstrument` value. + + The token expires after first use. + defaultPaymentInstrument: + description: Default payment instrument information. + anyOf: + - $ref: '#/components/schemas/VaultedInstrument' + - $ref: '#/components/schemas/AlternativePaymentInstrument' + - $ref: '#/components/schemas/CashInstrument' + - $ref: '#/components/schemas/CheckInstrument' + - type: 'null' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + primaryAddress: + description: Customer's primary address. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + company: + $ref: '#/components/schemas/Company' + averageValue: + $ref: '#/components/schemas/CustomerAverageValue' + paymentCount: + x-label: Payments + x-sortable: true + x-basic: true + description: Total number of approved payments made by the customer. + readOnly: true + type: integer + lastPaymentTime: + x-label: Last Payment + x-sortable: true + x-basic: true + description: Time and date of the customer's last approved payment. + type: + - string + - 'null' + format: date-time + readOnly: true + lifetimeRevenue: + $ref: '#/components/schemas/CustomerLifetimeRevenue' + invoiceCount: + x-sortable: true + description: >- + Total number of invoices issued to the customer. + + This value is auto-incrementing. + + If this value is `0`, the record relates to a lead. + + A lead is a customer who has not made a payment yet. + + If this value is greater than or equal to `1` the record relates to + a customer. + readOnly: true + type: integer + tags: + description: List of customer tags. + readOnly: true + type: array + items: + $ref: '#/components/schemas/Tag' + revision: + description: >- + Number of times the customer's data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + isEddRequired: + description: >- + Specifies if Enhanced Due Diligence (EDD) is enabled for this + customer. + + For more information, see [Enhanced Due + Diligence](https://www.rebilly.com/docs/kyc-and-aml/edd/). + type: boolean + default: false + hasFulfilledKyc: + description: >- + Specifies if the customer has accepted and reviewed identity and + address documents, or an accepted credit file document. + type: boolean + readOnly: true + organizationId: + readOnly: true + allOf: + - $ref: '#/components/schemas/OrganizationId' + taxNumbers: + type: + - array + - 'null' + description: Tax numbers of the customer. + items: + $ref: '#/components/schemas/TaxNumber' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - attachments + - defaultPaymentInstrument + - leadSource + - website + _embedded: + type: object + description: >- + Embedded objects that are requested using the `expand` query string + parameter. + readOnly: true + properties: + leadSource: + type: object + CustomerTimelineCustomEvent: + type: object + required: + - name + properties: + id: + type: string + description: ID of the customer timeline custom event. + readOnly: true + maxLength: 50 + example: tmln_0YV8Q9WEF5DTA8HFXS27D1G6GC + name: + description: |- + Name of the customer timeline custom event type. + This value must not conflict with any system event name. + type: string + minLength: 1 + maxLength: 255 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + LeadSourceData: + type: object + description: Lead source information. + properties: + medium: + description: >- + Category of the lead source traffic. + + For example, the medium could be organic search, Google ads, Display + ads, and so on. + type: + - string + - 'null' + maxLength: 512 + source: + description: 'Domain, platform, or channel from which the lead source originates.' + type: + - string + - 'null' + maxLength: 512 + campaign: + description: Campaign name of the lead source. + type: + - string + - 'null' + maxLength: 512 + term: + description: Term associated with a lead source. + type: + - string + - 'null' + maxLength: 512 + content: + description: |- + Content contained in the lead source content. + For example, content could be graphics, video, and so on. + type: + - string + - 'null' + maxLength: 512 + affiliate: + description: Individual or entity that is affiliated with the lead source. + type: + - string + - 'null' + maxLength: 512 + subAffiliate: + description: >- + Individual or entity that is associated with a lead source + affiliate. + + In other products, this field may also be referred to as sub ID or + click ID in some. + type: + - string + - 'null' + maxLength: 512 + salesAgent: + description: Name of the sales agent associated with the lead source. + type: + - string + - 'null' + maxLength: 512 + clickId: + description: >- + ID of the lead source click. + + This value is passed in the ad click URL for tracking and campaign + attribution. + type: + - string + - 'null' + maxLength: 512 + path: + description: URL from which the lead source originates. + type: + - string + - 'null' + maxLength: 512 + referrer: + description: >- + Lead source [`referrer` + URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer). + type: + - string + - 'null' + format: url + maxLength: 2083 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + LeadSource: + allOf: + - $ref: '#/components/schemas/LeadSourceData' + - type: object + properties: + original: + readOnly: true + allOf: + - $ref: '#/components/schemas/LeadSourceData' + RedemptionCancelTimelineAction: + description: Cancel redemption. + type: object + readOnly: true + properties: + action: + type: string + enum: + - redemption-cancel + redemptionId: + $ref: '#/components/schemas/ResourceId' + ResendEmailTimelineAction: + type: object + description: Resend email action. + readOnly: true + properties: + action: + type: string + enum: + - resend-email + messageId: + $ref: '#/components/schemas/ResourceId' + RulesetRestoreTimelineAction: + type: object + description: Restore ruleset version. + readOnly: true + properties: + action: + type: string + enum: + - ruleset-restore + version: + type: integer + description: Version of the ruleset to restore. + ShowEddSearchLogsTimelineAction: + type: object + description: Show Enhanced Due Diligence (EDD) search logs action. + readOnly: true + properties: + action: + type: string + enum: + - show-edd-search-logs + searchLogId: + $ref: '#/components/schemas/ResourceId' + type: + type: string + enum: + - arrest + - bankruptcy + - fraud + - occupation + TimelineAction: + type: object + discriminator: + propertyName: action + mapping: + redemption-cancel: '#/components/schemas/RedemptionCancelTimelineAction' + resend-email: '#/components/schemas/ResendEmailTimelineAction' + ruleset-restore: '#/components/schemas/RulesetRestoreTimelineAction' + show-edd-search-logs: '#/components/schemas/ShowEddSearchLogsTimelineAction' + oneOf: + - $ref: '#/components/schemas/RedemptionCancelTimelineAction' + - $ref: '#/components/schemas/ResendEmailTimelineAction' + - $ref: '#/components/schemas/RulesetRestoreTimelineAction' + - $ref: '#/components/schemas/ShowEddSearchLogsTimelineAction' + TimelineTable: + type: object + description: Additional timeline message information. + discriminator: + propertyName: type + mapping: + three-columns: '#/components/schemas/ThreeColumnsTimelineTable' + two-columns: '#/components/schemas/TwoColumnsTimelineTable' + one-column: '#/components/schemas/OneColumnTimelineTable' + list: '#/components/schemas/ListTimelineTable' + properties: + type: + type: string + enum: + - list + - one-columns + - two-columns + - three-columns + title: + type: string + description: Data table title. + footer: + type: string + description: Data table footer. + ThreeColumnsTimelineTable: + allOf: + - $ref: '#/components/schemas/TimelineTable' + - type: object + properties: + data: + type: array + description: Table data. Each array element represents a table row. + items: + type: object + properties: + attribute: + type: string + previousValue: + type: string + newValue: + type: string + example: + attribute: Descriptor + previousValue: test.com + newValue: new-test.com + TwoColumnsTimelineTable: + allOf: + - $ref: '#/components/schemas/TimelineTable' + - type: object + properties: + data: + type: array + description: Table data. Each array element represents a table row. + items: + type: object + properties: + attribute: + type: string + value: + type: string + example: + attribute: Who + value: John Doe + OneColumnTimelineTable: + allOf: + - $ref: '#/components/schemas/TimelineTable' + - type: object + properties: + data: + type: array + description: Table data. Each array element represents a table row. + items: + type: object + properties: + attribute: + type: string + value: + type: string + example: + attribute: Who + value: John Doe + ListTimelineTable: + allOf: + - $ref: '#/components/schemas/TimelineTable' + - type: object + properties: + data: + type: array + description: Table data. Each array element represents a line. + items: + type: string + TimelineExtraData: + type: object + description: Additional data. + readOnly: true + properties: + actions: + description: |- + Actions available for a timeline message. + If no actions are available, this field is empty. + type: array + minItems: 0 + items: + $ref: '#/components/schemas/TimelineAction' + tables: + description: Table data that is attached to the timeline message. + type: array + minItems: 0 + items: + $ref: '#/components/schemas/TimelineTable' + author: + type: object + description: Author of the timeline message. + properties: + userFullName: + description: Author's full name. + type: string + userId: + description: Author's user ID. + type: string + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + mentions: + description: 'User mentions, or tags, in a timeline.' + type: object + additionalProperties: + type: string + example: + '@test@mail.com': userId-1 + links: + description: Links that are attached to a timeline message. + type: array + items: + type: object + properties: + resourceType: + type: string + enum: + - kyc-document + - invoice + - subscription + - transaction + - email-message + - dispute + - coupon + - external + resourceId: + $ref: '#/components/schemas/ResourceId' + placeholder: + type: string + example: KYC document + externalUrl: + type: string + example: 'http://example.com' + ServerTimestamp: + type: string + description: Read-only timestamp in ISO 8601 date-time format. + format: date-time + readOnly: true + CustomerTimeline: + type: object + properties: + id: + type: string + description: ID of the customer timeline custom event. + readOnly: true + maxLength: 50 + example: tmln_0YV8Q9WEF5DTA8HFXS27D1G6GC + type: + description: Type of timeline message. + type: string + enum: + - account-password-reset-requested + - account-verification-requested + - aml-list-was-possibly-matched + - deposit-request-completed + - deposit-request-created + - deposit-request-expired + - deposit-request-started + - coupon-applied + - coupon-redeemed + - coupon-redemption-canceled + - custom-event + - custom-event-processed + - custom-fields-changed + - customer-bank-account-blocked + - customer-blocked + - customer-comment-created + - customer-created + - customer-merged + - customer-payment-card-blocked + - customer-requested-otp + - customer-tagged + - customer-untagged + - default-payment-instrument-changed + - email-message-sent + - experian-check-performed + - invoice-abandoned + - invoice-created + - invoice-disputed + - invoice-issued + - invoice-paid + - invoice-partially-paid + - invoice-partially-refunded + - invoice-past-due + - invoice-refunded + - invoice-reissued + - invoice-revenue-recognized + - invoice-voided + - kyc-document-accepted + - kyc-document-created + - kyc-document-modified + - kyc-document-rejected + - kyc-document-reviewed + - kyc-request-fulfilled + - lead-source-changed + - order-activated + - order-canceled + - order-churned + - order-completed + - order-created + - order-downgraded + - order-paid-early + - order-reactivated + - order-renewed + - order-upgraded + - payment-card-expiration-was-modified + - payment-card-expired + - payment-instrument-created + - payment-instrument-deactivated + - primary-address-changed + - quickbooks-customer-created + - quickbooks-customer-task-failed + - quote-canceled + - quote-created + - quote-expired + - quote-issued + - quote-order-attached + - quote-recalled + - quote-rejected + - quote-updated + - refund-was-reflected-in-invoices + - subscription-paused + - subscription-resumed + - subscription-trial-end-changed + - transaction-abandoned + - transaction-amount-discrepancy-found + - transaction-approved + - transaction-canceled + - transaction-declined + - transaction-discrepancy-found + - transaction-disputed + - transaction-reconciled + - transaction-refunded + - transaction-voided + - transaction-waiting-gateway + customEventType: + description: >- + Timeline custom event type. + + Used with `custom-event` type. + + This value must be defined using the [Create customer timeline + custom event type](../PostCustomerTimeline) operation. + type: + - string + - 'null' + minLength: 1 + maxLength: 255 + customData: + description: |- + Timeline custom event data. + Used with `custom-event` type. + Transforms to `extraData` two-column table in response. + type: object + writeOnly: true + example: + customAttribute: customValue + otherAttribute: otherValue + triggeredBy: + description: 'Specifies who, or what, triggered the timeline event.' + type: string + readOnly: true + enum: + - rebilly + - app + - direct-api + message: + description: Detailed message description. + type: string + extraData: + $ref: '#/components/schemas/TimelineExtraData' + occurredTime: + description: Date and time when the timeline message occurred. + $ref: '#/components/schemas/ServerTimestamp' + _links: + $ref: '#/components/schemas/SelfLink' + Dispute: + type: object + properties: + id: + type: string + description: ID of the dispute. + readOnly: true + maxLength: 50 + example: dp_0YVCE8J5F2DE58FV0S8YASW4HK + customerId: + description: ID of the customer who is disputing a transaction. + type: + - string + - 'null' + readOnly: true + maxLength: 50 + example: cus_0YV7DDSDD1C8DA64KHH2W33CPF + transactionId: + description: ID of the disputed transaction. + type: + - string + - 'null' + maxLength: 50 + example: txn_0YVDTQJ8YWDGQACV2N2N5SPWQ0 + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Dispute amount. + type: number + format: double + acquirerReferenceNumber: + description: Acquirer reference number for the dispute. + type: + - string + - 'null' + caseId: + description: Case ID of the dispute. + type: + - string + - 'null' + reasonCode: + description: >- + Code used in the chargeback that describes the reason for the + dispute. + type: + - string + - 'null' + maxLength: 255 + reasonDescription: + description: Description of the reason for the dispute. + type: + - string + - 'null' + maxLength: 512 + readOnly: true + category: + description: Category of dispute. + type: string + readOnly: true + enum: + - fraud + - authorization + - processing-errors + - consumer-disputes + - uncategorized + - bank-return + type: + description: Type of dispute. + type: string + enum: + - information-request + - first-chargeback + - second-chargeback + - arbitration + - fraud + - ethoca-alert + - verifi-alert + - bank-return + status: + description: Status of the dispute. + type: string + enum: + - response-needed + - under-review + - forfeited + - won + - lost + - unknown + postedTime: + description: Date and time when the dispute is posted. + type: string + format: date-time + deadlineTime: + description: >- + Latest date and time by when a merchant must submit a representment + for a dispute. + + If the deadline is missed, the merchant loses the dispute. + type: + - string + - 'null' + format: date-time + rawResponse: + description: >- + Raw response from the payment gateway that processed the disputed + transaction. + type: + - string + - 'null' + readOnly: true + resolvedTime: + type: + - string + - 'null' + format: date-time + description: Date and time when the dispute is resolved. + readOnly: true + revision: + description: |- + Number of times the dispute data has been modified. + + Use the revision number when analyzing webhook data to + determine if a change should take precedence over the current + representation. + type: integer + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - transaction + - attachments + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + transaction: + type: object + TransactionId: + type: string + description: ID of the transaction. + maxLength: 50 + example: txn_0YVDTQJ8YWDGQACV2N2N5SPWQ0 + ExternalIdentifier: + type: object + required: + - externalIdentifier + properties: + resource: + type: string + description: Type of the resource. + enum: + - customers + - invoices + - invoice-items + - transactions + - journal-accounts + - journal-entries + readOnly: true + resourceId: + type: string + description: ID of the resource entity. + readOnly: true + service: + type: string + description: Name of the external service. + enum: + - quickbooks-online + readOnly: true + externalIdentifier: + type: string + description: ID of the referenced entity within an external service. + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + description: Related links. + type: array + readOnly: true + items: + type: object + properties: + rel: + type: string + description: Type of link. + enum: + - self + - externalService + href: + type: string + description: Link URL. + ExternalServiceSettings: + type: object + description: Settings for external service integration. + properties: + quickBooksOnline: + type: object + title: QuickBooksOnlineExternalServiceSettings + description: QuickBooks Online settings. + properties: + customer: + type: object + title: QuickBooksOnlineCustomerExternalServiceSettings + description: Customer settings. + properties: + sync: + type: string + description: >- + Specifies how to synchronize customers between your Rebilly + and QuickBooks Online account. + default: manually + enum: + - manually + - when-used + - always + x-enumDescriptions: + manually: >- + Customer data is only synchronized when it is manually + executed. + when-used: >- + Customer data is synchronized when related entities, such + as transactions or invoices are created or updated. + always: >- + Customer data is synchronized automatically as data is + created or updated. + displayName: + type: string + default: full-name + enum: + - id + - full-name + - organization-name + invoice: + type: object + title: QuickBooksOnlineInvoiceExternalServiceSettings + description: Invoice settings. + properties: + sync: + type: string + description: >- + Specifies how to synchronize invoices between your Rebilly + and QuickBooks Online account. + default: manually + enum: + - manually + - when-issued + - always + x-enumDescriptions: + manually: >- + Invoice data is only synchronized when it is manually + executed. + when-issued: Invoice data is synchronized when issued. + always: >- + Invoice data is synchronized automatically as data it is + issued or updated. + unearnedRevenueAccountId: + type: string + description: ID of the journal account where all products are created. + example: jrn_acc_01H32J1C97KWGRCKF1E1JY0QG8 + taxesAccountId: + type: + - string + - 'null' + default: null + description: ID of the journal account where all taxes are created. + example: jrn_acc_01H32J1KGXSRDH0K8Q7YYA552T + department: + type: + - string + - 'null' + default: null + description: >- + ID of the QuickBooks Online department that invoices are + related to. + example: jrn_acc_01H32J1KGXSRDH0K8Q7YYA552T + itemName: + type: string + description: Name of the invoice item. + itemDescription: + type: string + description: Description of the invoice item. + itemSku: + type: string + description: Stock Keeping Unit (SKU) of the invoice item. + itemLineDescription: + type: string + description: Description of the invoice line item. + taxName: + type: string + description: Name of the tax that is applied to the invoice. + taxDescription: + type: string + description: Description of the tax that is applied to the invoice. + taxSku: + type: string + description: >- + Stock Keeping Unit (SKU) of the tax that is applied to the + invoice. + taxLineDescription: + type: string + description: Description of the tax line. + transaction: + type: object + title: QuickBooksOnlineTransactionExternalServiceSettings + description: Transaction settings. + properties: + syncPayments: + type: string + description: >- + Specifies how to synchronize transactions between your + Rebilly and QuickBooks Online account. + default: manually + enum: + - manually + - always + x-enumDescriptions: + manually: >- + Transaction data is only synchronized when it is manually + executed. + always: >- + Transaction data is synchronized automatically as data is + created or updated. + syncRefundReceipts: + type: string + description: >- + Specifies how to synchronize invoices between your Rebilly + and QuickBooks Online account. + default: manually + enum: + - manually + - always + x-enumDescriptions: + manually: >- + Invoice data is only synchronized when it is manually + executed. + always: >- + Invoice data is synchronized automatically as data is + created or updated. + depositAccountId: + type: string + description: >- + ID of the journal account where all payments and refund + receipts are deposited. + example: jrn_acc_01H32J1C97KWGRCKF1E1JY0QG8 + department: + type: + - string + - 'null' + default: null + description: >- + ID of the QuickBooks Online department that payments and + refund receipts are related to. + example: jrn_acc_01H32J1KGXSRDH0K8Q7YYA552T + journalEntry: + type: object + title: QuickBooksOnlineJournalEntryExternalServiceSettings + description: Journal entry settings. + properties: + sync: + type: string + description: >- + Specifies how to synchronize journal entries between your + Rebilly and QuickBooks Online account. + default: manually + enum: + - manually + - always + x-enumDescriptions: + manually: >- + Journal entry data is only synchronized when it is + manually executed. + always: >- + Journal entry data is synchronized automatically as data + is created or updated. + File: + type: object + properties: + id: + type: string + description: ID of the file. + readOnly: true + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + name: + description: Original file name. + type: string + extension: + description: File extension. + type: string + description: + description: Description of the file. + type: string + sourceType: + description: Source of the file. + type: + - string + - 'null' + enum: + - upload + - camera + - organization-export + - organization-closure-export + - null + example: upload + tags: + description: List of tags associated with the file. + type: array + items: + type: string + mime: + description: MIME type of the file. + type: string + readOnly: true + enum: + - image/png + - image/jpeg + - image/gif + - application/pdf + size: + description: 'File size, in bytes.' + type: integer + readOnly: true + width: + description: Image width. This field applicable to images only. + type: integer + readOnly: true + height: + description: Image height. This field applicable to images only. + type: integer + readOnly: true + sha1: + description: Hash sum of the file. + type: string + readOnly: true + exifData: + description: >- + Collection of EXIF tags contained in the image metadata. This field + applicable to images only. + type: + - object + - 'null' + readOnly: true + example: + FileSize: 120 + Software: GIMP 2.4.5 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + isPublic: + description: >- + Specifies if the file is available publicly without authentication. + + If this value is `true`, the permalink in the `_links` section + contains the public URL. + type: boolean + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - download + - signedLink + - permalink + FileCreateFromInline: + type: object + required: + - file + properties: + file: + description: File in base64 encoded format. + type: string + example: R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs= + isPublic: + description: >- + Specifies if the file is available publicly without authentication. + + If this value is `true`, the permalink in the `_links` section + contains the public URL. + type: boolean + example: false + name: + description: File name used for downloading. + type: string + example: logo.png + description: + description: Description of the file. + type: string + example: My file description + sourceType: + description: Source of the file. + type: + - string + - 'null' + enum: + - upload + - camera + example: upload + tags: + description: List of tags associated with the file. + type: array + items: + type: string + example: + - test + - tags + FileCreateFromUrl: + type: object + required: + - url + properties: + url: + description: URL of the file to upload. + type: string + example: >- + https://blog.rebilly.com/wp-content/uploads/2017/09/rb_LogoInverted_Small.png + isPublic: + description: >- + Specifies if the file is available publicly without authentication. + + If this value is `true`, the permalink in the `_links` section + contains the public URL. + type: boolean + example: false + name: + description: File name used for downloading. + type: string + example: logo.png + description: + description: Description of the file. + type: string + example: My file description + sourceType: + description: Source of the file. + type: + - string + - 'null' + enum: + - upload + - camera + example: upload + tags: + description: List of tags associated with the file. + type: array + items: + type: string + example: + - test + - tags + ApplicationInstance: + type: object + description: Application instance object. + properties: + status: + description: Status of the application instance. + readOnly: true + type: string + enum: + - enabling + - enabled + - disabling + - disabled + isConfigured: + description: >- + Determines if the configuration of the application instance is + complete. + + Configuration is completed using the + [PutApplicationInstanceConfiguration](https://all-rebilly.redoc.ly/tag/Application-users/operation/PutApplicationInstanceConfiguration) + operation. + type: boolean + readOnly: true + settings: + description: Configuration settings that are available during installation. + type: object + additionalProperties: + type: string + description: >- + Additional properties that the user may want to add when + installing an application. + example: + color: red + limit: '5' + deprecated: true + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + ApplicationInstanceConfiguration: + type: object + required: + - settings + description: >- + Application instance configuration. + + + If the `configuredBy` field is set to `user`, only user can change + application settings. + + + If the `configuredBy` field is set to `application`, only the + application itself can change settings. + properties: + settings: + description: Configuration settings that are available during installation. + type: object + additionalProperties: + type: string + description: >- + Additional properties that the user may want to add when + installing an application. + example: + color: red + level: gold + _links: + readOnly: true + type: array + description: Related links. + items: + type: object + properties: + rel: + type: string + description: Type of link. + enum: + - self + - configuration + x-enumDescriptions: + self: Link to the application instance configuration. + configuration: >- + Link to the external resource where the application instance + must be configured. + + This field contains a JWT in the query parameter. + + Direct the user to this URL where application settings must + be submitted. + href: + type: string + description: Link URL. + Application: + type: object + description: Application object. + required: + - name + - logoId + - authorName + - tagline + - description + - permissions + properties: + id: + type: string + description: ID of the application. + readOnly: true + maxLength: 50 + example: app_01H0HT25X7XXCJGS4H8VJSYF2Y + name: + description: Name of the application. + type: string + logoId: + type: string + description: File ID of the application logo. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + authorName: + description: Name of the application author. + type: string + authorLogoId: + type: + - string + - 'null' + description: File ID of the author's logo. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + tagline: + description: Tagline of the application. + type: string + description: + description: >- + Detailed application description. This field accepts markdown and + simple text. + type: string + status: + description: Status of the application. + readOnly: true + type: string + enum: + - pending-approval + - available + - disabled + permissions: + description: >- + List of permissions that are available to the application. + + If you would like to add permissions, contact [Rebilly + Support](mailto:info@rebilly.com). + type: array + items: + type: string + format: operationId + enum: + - DeleteApplicationInstance + - GetApplicationInstanceConfiguration + - GetCustomer + - GetCustomerCollection + - GetInvoice + - GetInvoiceCollection + - GetPlan + - GetPlanCollection + - GetProduct + - GetProductCollection + - GetSubscription + - GetSubscriptionCollection + - GetTransaction + - GetTransactionCollection + - GetWebhook + - GetWebsite + - GetWebsiteCollection + - PostCoupon + - PostCouponRedemption + - PostGatewayAccountDowntimeSchedule + - PostPayout + - PostServiceCredential + - PostWebhook + - PostWebhookCredentialHash + - PutApplicationInstanceConfiguration + - PutCoupon + example: + - GetInvoice + - GetInvoiceCollection + - GetCustomer + - GetCustomerCollection + configurationUrl: + description: >- + External URL where the application is configured during + installation. + type: + - string + - 'null' + format: url + configuredBy: + type: string + description: Specifies who can configure the application. + readOnly: true + enum: + - user + - application + properties: + description: >- + Defines settings that users can complete when they install the + application. + + This field accepts [JSON-schema](https://json-schema.org/) drafts 4, + 6, and 7. + type: object + example: + type: object + properties: + email: + type: string + max: + type: integer + minimum: 0 + exclusiveMaximum: 100 + required: + - email + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - applicationInstance + - logoUrl + - authorLogoUrl + UserApplication: + allOf: + - $ref: '#/components/schemas/Application' + - properties: + _embedded: + type: object + description: >- + Embedded objects that are requested by the `expand` query + parameter. + readOnly: true + properties: + applicationInstance: + $ref: '#/components/schemas/ApplicationInstance' + OwnerApplicationInstance: + allOf: + - $ref: '#/components/schemas/ApplicationInstance' + - type: object + properties: + organizationId: + readOnly: true + allOf: + - $ref: '#/components/schemas/OrganizationId' + token: + description: Session token used to authenticate an organization. + type: string + readOnly: true + ManualShipping: + description: Manually set shipping amount. + type: object + required: + - amount + - calculator + properties: + amount: + x-type: Money + type: number + format: double + description: Shipping amount. + calculator: + description: Shipping calculator. + type: string + enum: + - manual + RebillyShipping: + description: Rebilly-based shipping which is calculated by shipping rates. + type: object + required: + - calculator + properties: + calculator: + description: Shipping calculator. + type: string + enum: + - rebilly + rateId: + description: >- + ID of the shipping rate. If unset the cheapest applicable shipping + rate is chosen. + type: + - string + - 'null' + maxLength: 50 + example: shipping-123-456 + amount: + type: number + format: double + readOnly: true + description: >- + Shipping amount which is calculated from [Shipping + rates](https://all-rebilly.redoc.ly/tag/Shipping-rates). + Shipping: + description: Shipping settings. + type: object + discriminator: + propertyName: calculator + mapping: + manual: '#/components/schemas/ManualShipping' + rebilly: '#/components/schemas/RebillyShipping' + anyOf: + - $ref: '#/components/schemas/ManualShipping' + - $ref: '#/components/schemas/RebillyShipping' + TaxItem: + type: object + required: + - amount + - description + properties: + amount: + description: Amount of the tax. + type: number + format: double + description: + type: string + description: Description of the tax. + rate: + description: >- + Overall sales tax rate which includes state, county, city and + district tax. + type: + - number + - 'null' + format: double + readOnly: true + stateAmount: + description: Amount of sales tax to collect for the state. + type: + - number + - 'null' + format: double + example: 0.94 + readOnly: true + countyAmount: + description: Amount of sales tax to collect for the county. + type: + - number + - 'null' + format: double + example: 0.04 + readOnly: true + cityAmount: + description: Amount of sales tax to collect for the city. + type: + - number + - 'null' + format: double + example: 0 + readOnly: true + specialDistrictAmount: + description: Amount of sales tax to collect for the special district. + type: + - number + - 'null' + format: double + example: 0.38 + readOnly: true + stateRate: + description: State sales tax rate for given location. + type: + - number + - 'null' + format: double + readOnly: true + countyRate: + description: County sales tax rate for given location. + type: + - number + - 'null' + format: double + readOnly: true + cityRate: + description: City sales tax rate for given location. + type: + - number + - 'null' + format: double + readOnly: true + specialDistrictRate: + description: Special district sales tax rate for given location. + type: + - number + - 'null' + format: double + readOnly: true + jurisdictions: + description: Jurisdiction names for the invoice. + type: + - object + - 'null' + readOnly: true + properties: + country: + description: Two-letter ISO country code for the provided location. + type: + - string + - 'null' + example: US + readOnly: true + state: + description: Postal abbreviated state name for the provided location. + type: + - string + - 'null' + example: CA + readOnly: true + county: + description: County name for the provided location. + type: + - string + - 'null' + example: LOS ANGELES + readOnly: true + city: + description: City name for the provided location. + type: + - string + - 'null' + example: LOS ANGELES + readOnly: true + ManualTax: + type: object + description: Manual tax calculator. + required: + - calculator + - items + properties: + calculator: + description: Type of tax calculator. + type: string + enum: + - manual + amount: + type: integer + x-type: Money + x-sortable: true + description: Total amount of taxes. + readOnly: true + items: + type: array + description: Taxes applied. + items: + $ref: '#/components/schemas/TaxItem' + RebillyTaxJarTax: + type: object + description: TaxJar tax calculator. + required: + - calculator + properties: + calculator: + description: Type of tax calculator. + type: string + enum: + - rebilly-taxjar + amount: + type: integer + x-type: Money + x-sortable: true + description: Total amount of taxes. + readOnly: true + items: + type: array + description: >- + Taxes applied and calculated based on the `taxCategoryId` of the + related product. + items: + $ref: '#/components/schemas/TaxItem' + RebillyAvalaraTax: + type: object + description: Avalara tax calculator. + required: + - calculator + properties: + calculator: + description: Type of tax calculator. + type: string + enum: + - rebilly-avalara + amount: + type: integer + x-type: Money + x-sortable: true + description: Total amount of taxes. + readOnly: true + items: + type: array + description: >- + Taxes applied and calculated based on the `taxCategoryId` of the + related product. + items: + $ref: '#/components/schemas/TaxItem' + Taxes: + description: Taxes. + type: object + discriminator: + propertyName: calculator + mapping: + manual: '#/components/schemas/ManualTax' + rebilly-taxjar: '#/components/schemas/RebillyTaxJarTax' + rebilly-avalara: '#/components/schemas/RebillyAvalaraTax' + anyOf: + - $ref: '#/components/schemas/ManualTax' + - $ref: '#/components/schemas/RebillyTaxJarTax' + - $ref: '#/components/schemas/RebillyAvalaraTax' + InvoiceItem: + type: object + required: + - type + - unitPrice + properties: + id: + type: string + description: ID of the invoice item. + readOnly: true + maxLength: 50 + example: ii_0YVFDEQS2KCFTBN9HXWJFY55GV + type: + description: Type of invoice item. + type: string + x-basic: true + enum: + - debit + - credit + description: + description: Description of the invoice item. + type: string + maxLength: 1000 + unitPrice: + description: Unit price of the invoice item. + type: number + format: double + quantity: + description: Quantity of the invoice item. + type: integer + price: + description: Total price of the invoice item. + type: number + format: double + readOnly: true + productId: + type: + - string + - 'null' + description: ID of the product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + planId: + type: + - string + - 'null' + description: ID of the plan. + maxLength: 50 + readOnly: true + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + subscriptionId: + type: + - string + - 'null' + description: ID of the order. + maxLength: 50 + readOnly: true + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + discountAmount: + description: Discount amount applied to the invoice item. + type: number + format: double + readOnly: true + periodStartTime: + description: Date and time when the billing period starts. + type: + - string + - 'null' + format: date-time + periodEndTime: + description: Date and time when the billing period ends. + type: + - string + - 'null' + format: date-time + periodNumber: + description: >- + Billing period number that is associated with the invoice item. + + For example, an invoice item for a service is included in billing + period number 3. + + The invoice item is only applied to billing period number 3. + type: + - integer + - 'null' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + tax: + description: Invoice item tax. + readOnly: true + oneOf: + - $ref: '#/components/schemas/TaxItem' + - type: 'null' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - product + - subscription + - plan + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + product: + type: object + plan: + type: object + GatewayName: + description: Payment gateway name. + type: + - string + - 'null' + enum: + - A1Gateway + - ACI + - Adyen + - Aircash + - Airpay + - Airwallex + - AmazonPay + - AmexVPC + - ApcoPay + - AsiaPaymentGateway + - AstroPayCard + - AuthorizeNet + - Awepay + - Bambora + - BankSEND + - BitPay + - BlueSnap + - BraintreePayments + - Buckaroo + - Cardknox + - Cashflows + - CASHlib + - Cashterminal + - CashToCode + - CauriPayment + - Cayan + - CCAvenue + - Chase + - CheckoutCom + - Chillstock + - Circle + - Citadel + - Clearhaus + - Cleo + - CODVoucher + - Coinbase + - CoinGate + - CoinPayments + - Conekta + - Coppr + - Credorax + - Cryptonator + - CyberSource + - DataCash + - Dengi + - Dimoco + - Directa24 + - dLocal + - Dragonphoenix + - Dropayment + - EasyPayDirect + - EBANX + - ecoPayz + - EcorePay + - Elavon + - Euteller + - eMerchantPay + - EMS + - ePay + - EPG + - EPro + - Ezeebill + - eZeeWallet + - ezyEFT + - FasterPay + - Finrax + - Flexepin + - FinTecSystems + - FundSend + - Forte + - GET + - Gigadat + - GlobalOnePay + - Gooney + - Gpaysafe + - Greenbox + - HiPay + - iCanPay + - ICEPAY + - iCheque + - iDebit + - Ilixium + - Ingenico + - INOVAPAY + - Inovio + - Intuit + - InstaDebit + - IpayOptions + - JetPay + - Jeton + - JPMOrbital + - Khelocard + - Klarna + - Konnektive + - loonie + - LPG + - MaxiCash + - MercadoPago + - MiFinity + - MobilePay + - Moneris + - MtaPay + - MuchBetter + - MuchBetterGateway + - MyFatoorah + - Neosurf + - Netbanking + - Neteller + - NGenius + - NinjaWallet + - NMI + - NordikCoin + - NOWPayments + - NuaPay + - OchaPay + - Onlineueberweisen + - OnRamp + - Orbital + - Pagadito + - Pagsmile + - Panamerican + - ParamountCommerce + - ParamountEft + - ParamountInterac + - PandaGateway + - Pay4Fun + - PayCash + - PayClub + - PayEcards + - Payeezy + - Payflow + - Paynote + - PaymentAsia + - PaymenTechnologies + - PaymentsOS + - Paymero + - PayPal + - Payper + - Payr + - PayRedeem + - PayRetailers + - Paysafe + - Paysafecard + - Paysafecash + - PayTabs + - PayULatam + - Payvision + - PharosPayments + - Piastrix + - Pin4Pay + - Plugnpay + - PostFinance + - PPRO + - Prosa + - PSiGate + - Rapyd + - Realex + - Realtime + - Redsys + - Rotessa + - RPN + - Safecharge + - SaltarPay + - Sagepay + - SeamlessChex + - SecureTrading + - SecurionPay + - Skrill + - SmartInvoice + - SMSVoucher + - Sofort + - SparkPay + - StaticGateway + - STPMexico + - Stripe + - Telr + - TestProcessor + - ToditoCash + - Truevo + - TrustsPay + - Trustly + - TWINT + - UPayCard + - USAePay + - VantivLitle + - vegaaH + - VCreditos + - VegaWallet + - Wallet88 + - Walpay + - WesternUnion + - Wirecard + - WorldlineAtosFrankfurt + - Worldpay + - XPay + - Zimpler + - Zotapay + AcquirerName: + description: Acquirer name. + type: + - string + - 'null' + enum: + - Adyen + - ACI + - Alipay + - AIB + - Aircash + - Airpay + - AmazonPay + - ApcoPay + - AsiaPaymentGateway + - AstroPay Card + - Awepay + - Ipay Options + - B+S + - Bambora + - BitPay + - Bank of America + - Bank of Moscow + - Bank of Rebilly + - Bank One + - BankSEND + - BMO Harris Bank + - Borgun + - BraintreePayments + - Buckaroo + - Cardknox + - CASHlib + - Cashterminal + - CashToCode + - Catalunya Caixa + - CCAvenue + - Chase + - CheckoutCom + - Chillstock + - ChinaUnionPay + - CIM + - Circle + - Citadel + - Clearhaus + - Cleo + - CODVoucher + - Coinbase + - CoinGate + - CoinPayments + - Conekta + - Coppr + - Credorax + - Cryptonator + - CyberSource + - Dimoco + - dLocal + - Dragonphoenix + - Dropayment + - EasyPayDirect + - EBANX + - ecoPayz + - EcorePay + - Elavon + - EMS + - ePay + - EPG + - Euteller + - Ezeebill + - eZeeWallet + - ezyEFT + - Fifth Third Bank + - Finrax + - First Data Buypass + - First Data Nashville + - First Data North + - First Data Omaha + - FinTecSystems + - Flexepin + - Forte + - FundSend + - Gigadat + - Global East + - Gooney + - Gpaysafe + - Heartland + - HiPay + - HSBC + - iCanPay + - ICEPAY + - iCheque + - Ilixium + - Ingenico + - INOVAPAY + - Intuit + - Jeton + - JPMOrbital + - Khelocard + - Klarna + - Konnektive + - loonie + - LPG + - Masapay + - MaxiCash + - MercadoPago + - Merrick + - Mission Valley Bank + - MiFinity + - MobilePay + - Moneris + - MuchBetter + - MuchBetterGateway + - MyFatoorah + - NATWEST + - Neosurf + - Netbanking + - Neteller + - NinjaWallet + - NMI + - NordikCoin + - NOWPayments + - NuaPay + - Nuvei + - OchaPay + - Onlineueberweisen + - OnRamp + - Orbital + - Other + - Panamerican + - Panda Bank + - Paramount + - ParamountCommerce + - ParamountEft + - ParamountInterac + - Pay4fun + - PayCash + - PayClub + - PayEcards + - PaymentAsia + - PaymenTechnologies + - PaymentsOS + - Paymero + - Paynetics + - PayPal + - Payper + - Payr + - PayRedeem + - PayRetailers + - PayTabs + - PayULatam + - Payvision + - PharosPayments + - Piastrix + - Pin4Pay + - Peoples Trust Company + - PostFinance + - PPRO + - Privatbank + - Prosa + - PSiGate + - QQPay + - Rapyd + - RBC + - RBS WorldPay + - RealTime + - Rotessa + - Safecharge + - SaltarPay + - SecureTrading + - SecurionPay + - Skrill + - SmartInvoice + - SMSVoucher + - Sofort + - SparkPay + - State Bank of Mauritius + - STPMexico + - Stripe + - TBI + - Telr + - TestProcessor + - ToditoCash + - Truevo + - Trustly + - TrustPay + - TrustsPay + - TSYS + - TWINT + - UPayCard + - Vantiv + - VCreditos + - VegaWallet + - VoicePay + - Wallet88 + - WeChat Pay + - Wells Fargo + - Wing Hang Bank + - Wirecard + - WorldPay + - XPay + - Zimpler + - Zotapay + MoneyAmount: + type: number + format: double + example: 10 + x-type: Money + Money: + type: object + required: + - amount + - currency + properties: + amount: + $ref: '#/components/schemas/MoneyAmount' + currency: + $ref: '#/components/schemas/CurrencyCode' + LanguageIsoCode: + type: string + description: Language in ISO 639-1 code format. + pattern: '[a-zA-Z]{2}' + example: US + PurchaseBumpOffer: + type: + - object + - 'null' + description: Purchase bump offer. + required: + - offerId + - offerType + - bumpAmount + properties: + offerId: + description: ID of the bump offer. + type: string + offerType: + description: Type of bump offer. + type: string + example: bonus + bumpAmount: + description: Amount of the bump offer. + $ref: '#/components/schemas/MoneyAmount' + bumpAmountInUsd: + description: Amount of the bump offer in USD. + readOnly: true + allOf: + - $ref: '#/components/schemas/MoneyAmount' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + HttpHeaders: + type: object + description: HTTP headers. + additionalProperties: + type: string + example: + Content-Type: application/json + Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' + x-is-free-form: true + RiskMetadata: + type: object + title: Risk metadata + description: Risk metadata used for 3D Secure and risk scoring. + properties: + ipAddress: + description: Customer's IP address. + type: + - string + - 'null' + format: ipv4 or ipv6 + example: 93.92.91.90 + fingerprint: + description: >- + Customer's device fingerprint. + + A device fingerprint is a unique token that is used to identify the + customer. + + The device fingerprint is generated based on device attributes, such + as: hardware, + + software, IP address, language, browser, and more. + type: + - string + - 'null' + maxLength: 50 + example: pIUt3xbgX3l9g3YDiLbx + httpHeaders: + oneOf: + - $ref: '#/components/schemas/HttpHeaders' + - type: 'null' + browserData: + type: + - object + - 'null' + description: Browser data used for 3D Secure and risk scoring. + required: + - colorDepth + - isJavaEnabled + - language + - screenWidth + - screenHeight + - timeZoneOffset + properties: + colorDepth: + description: >- + Browser color depth in bits per pixel. This value is obtained + using the `screen.colorDepth` property. + type: integer + minimum: 1 + maximum: 48 + example: 24 + isJavaEnabled: + description: >- + Specifies if Java is enabled in a browser. This value is + obtained from the `navigator.javaEnabled` property. + type: boolean + language: + description: >- + Browser language settings. This value is obtained from the + `navigator.language` property. + type: string + example: en-US + maxLength: 8 + screenWidth: + description: >- + Width of the browser screen. This value is obtained from the + `screen.width` property. + type: integer + minimum: 0 + maximum: 65535 + example: 1920 + screenHeight: + description: >- + Height of the browser screen. This value is obtained from the + `screen.height` property. + type: integer + minimum: 0 + maximum: 65535 + example: 1080 + timeZoneOffset: + description: >- + Browser time zone offset in minutes from UTC. + + A positive offset indicates that the local time is behind UTC. + + A negative offset indicates that the local time is ahead of UTC. + + You can find this value using the `(new + Date()).getTimezoneOffset()` property. + type: integer + minimum: -1410 + maximum: 1410 + example: 300 + extraData: + type: + - object + - 'null' + description: Third-party data used for risk scoring. + properties: + kountFraudSessionId: + description: Alpha-numeric `fraudSessionId` as provided by the Kount SDK. + type: string + minimum: 10 + maximum: 32 + example: abcdefg12345abababab123456789012 + payPalMerchantSessionId: + description: >- + PayPal `MerchantSessionID` as generated by the PayPal Fraudnet + SDK. + type: string + minimum: 1 + maximum: 64 + example: dd65ratxc5qv15iph3vyoq7l6davuowa + threatMetrixSessionId: + description: >- + Temporary identifier that is unique to the visitor's session and + passed to ThreatMetrix. + pattern: '[a-zA-Z0-9_-]+' + type: string + minimum: 1 + maximum: 128 + example: dd65ratxc5qv15iph3vyoq7l6davuowadd65ratxc5qv15iph3vyoq7l6davuowa + isProxy: + description: Specifies if the customer's IP address is related to a proxy. + type: boolean + readOnly: true + isVpn: + description: Specifies if the customer's IP address is related to a VPN. + type: boolean + readOnly: true + isTor: + description: Specifies if the customer's IP address is related to TOR. + type: boolean + readOnly: true + isHosting: + description: Specifies if the customer's IP address is related to hosting. + type: boolean + readOnly: true + hostingName: + description: 'Name of the data center or hosting provider, if available.' + type: + - string + - 'null' + readOnly: true + isp: + description: 'Internet Service Provider (ISP) name, if available.' + type: + - string + - 'null' + readOnly: true + country: + description: Country ISO Alpha-2 code of the specified IP address. + maxLength: 2 + type: + - string + - 'null' + readOnly: true + example: US + region: + description: Region of the specified IP address. + type: + - string + - 'null' + readOnly: true + example: NY + city: + description: City of the specified IP address. + type: + - string + - 'null' + readOnly: true + example: New York + latitude: + description: Latitude of the specified IP address. + type: number + format: double + readOnly: true + longitude: + description: Longitude of the specified IP address. + type: + - number + - 'null' + format: double + readOnly: true + postalCode: + description: Postal code of the specified IP address. + type: + - string + - 'null' + maxLength: 10 + readOnly: true + timeZone: + description: Time zone of the specified IP address. + type: + - string + - 'null' + readOnly: true + example: America/New_York + accuracyRadius: + description: 'Accuracy radius of the specified IP address, in kilometers.' + type: + - integer + - 'null' + readOnly: true + distance: + description: >- + Distance between the customer's IP address and the billing address + geolocation, in kilometers. + type: + - integer + - 'null' + readOnly: true + hasMismatchedBillingAddressCountry: + description: >- + Specifies if the customer's billing address country and geo-IP + address are not the same. + type: boolean + readOnly: true + hasMismatchedBankCountry: + description: >- + Specifies if the customer's bank country and geo-IP address are not + the same. + type: boolean + readOnly: true + hasMismatchedTimeZone: + description: >- + Specifies if the customer's browser time zone and the IP address + associated time zone are not the same. + type: boolean + readOnly: true + hasMismatchedHolderName: + description: >- + Specifies if the customer's billing address name and primary address + name are not the same. + type: boolean + readOnly: true + hasFakeName: + description: Specifies if the holder name seems fake. + type: boolean + readOnly: true + isHighRiskCountry: + description: >- + Specifies if the geo-IP country, or the customer's billing country, + is considered a high risk country. + type: boolean + readOnly: true + paymentInstrumentVelocity: + description: >- + Number of transactions for this payment instrument, based on + fingerprint, in the last 24 hours. + type: integer + readOnly: true + declinedPaymentInstrumentVelocity: + description: >- + Number of declined transactions for this payment instrument + fingerprint in the last 24 hours. + type: integer + readOnly: true + deviceVelocity: + description: >- + Number of transactions for this device, based on fingerprint, in the + last 24 hours. + type: integer + readOnly: true + ipVelocity: + description: Number of transactions for this IP address in the last 24 hours. + type: integer + readOnly: true + emailVelocity: + description: Number of transactions for this email address in the last 24 hours. + type: integer + readOnly: true + billingAddressVelocity: + description: >- + Number of transactions for this billing address in the last 24 + hours. + type: integer + readOnly: true + score: + description: Computed risk score based on all factors. + type: integer + readOnly: true + TransactionLimitAmount: + type: + - object + - 'null' + title: LimitAmount + description: Transaction amount limit information. + properties: + amount: + type: number + format: double + example: 275.35 + description: Limit amount. + currency: + $ref: '#/components/schemas/CurrencyCode' + resetTime: + type: + - string + - 'null' + format: date-time + description: |- + Date and time in which the limit amount resets. + This value may be used for user interfaces. + Transaction: + type: object + description: Transaction information. + properties: + id: + readOnly: true + $ref: '#/components/schemas/TransactionId' + websiteId: + readOnly: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + customerId: + x-basic: true + x-sortable: true + allOf: + - $ref: '#/components/schemas/CustomerId' + type: + description: Type of transaction. + type: string + x-basic: true + readOnly: true + enum: + - 3ds-authentication + - authorize + - capture + - credit + - refund + - sale + - setup + - void + status: + description: Status of the transaction. + type: string + readOnly: true + enum: + - completed + - conn-error + - disputed + - never-sent + - offsite + - partially-refunded + - pending + - refunded + - sending + - timeout + - voided + - waiting-approval + - waiting-capture + - waiting-gateway + - waiting-refund + result: + description: Result of the transaction. + type: string + x-basic: true + readOnly: true + enum: + - abandoned + - approved + - canceled + - declined + - unknown + amount: + x-type: Money + x-sortable: true + x-basic: true + description: Total amount of the transaction. + type: number + format: double + readOnly: true + currency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + purchaseAmount: + description: >- + Amount by which the purchase is completed. + + If an adjustment occurs, the purchased amount may differ from the + requested amount. + type: number + format: double + x-type: Money + x-currency-field: purchaseCurrency + x-sortable: true + readOnly: true + purchaseCurrency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + requestAmount: + description: |- + Amount of the payment request. + If an adjustment occurs, + the purchase amount may differ from the billing amount. + type: number + x-type: Money + x-currency-field: requestCurrency + format: double + readOnly: true + requestCurrency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + parentTransactionId: + description: ID of the parent transaction. + type: + - string + - 'null' + $ref: '#/components/schemas/TransactionId' + childTransactions: + description: IDs of child transactions. + readOnly: true + type: array + items: + $ref: '#/components/schemas/ResourceId' + invoiceIds: + description: Related invoice IDs. + readOnly: true + type: array + items: + $ref: '#/components/schemas/ResourceId' + subscriptionIds: + description: Subscription IDs of invoices that are related to the transaction. + readOnly: true + type: array + items: + $ref: '#/components/schemas/ResourceId' + planIds: + description: Plan IDs of orders that are related to the transaction. + readOnly: true + type: array + items: + $ref: '#/components/schemas/ResourceId' + isRebill: + description: >- + Specifies if the transaction is one of a number of recurring + payments in a subscription, excluding trials or setup fees. + type: boolean + readOnly: true + rebillNumber: + description: >- + Rebill number of the transaction. + + A rebill number is the number of recurring payments in a + subscription, excluding trials or setup fees. + type: integer + readOnly: true + x-sortable: true + billingAddress: + description: Billing address. + $ref: '#/components/schemas/ContactObject' + has3ds: + description: Specifies if the transaction uses 3D Secure. + type: boolean + readOnly: true + 3ds: + type: object + description: Authentication object. + readOnly: true + properties: + server: + description: Name of the 3D Secure server. + type: string + version: + description: Version of 3D Secure. + type: string + enum: + - 1.0.2 + - 2.1.0 + - 2.2.0 + enrolled: + description: Specifies if the cardholder is enrolled in 3D Secure. + type: string + enum: + - 'yes' + - 'no' + - invalid card/timeout + - unavailable + authenticated: + description: Authentication response status for 3D Secure. + type: string + enum: + - 'yes' + - 'no' + - not applicable + - attempted + liability: + type: string + enum: + - protected + - not protected + - protected (attempt) + flow: + description: Authentication flow for 3D Secure 2. + type: string + enum: + - frictionless + - challenge + isDowngraded: + description: >- + Specifies if 3D Secure 2 is attempted and downgraded to 3D + Secure 1. + type: boolean + default: false + deprecated: true + redirectUrl: + description: >- + URL where the end-user is redirected to when an offsite transaction + is completed. + + The default value is the website URL. + type: + - string + - 'null' + format: uri + retryNumber: + type: integer + readOnly: true + description: Position of the transaction in the sequence of retries. + x-sortable: true + isRetry: + type: boolean + readOnly: true + description: Specifies if a transaction is a retry. + billingDescriptor: + type: + - string + - 'null' + readOnly: true + description: >- + Billing descriptor that appears on the periodic billing statement. + + For a credit card statement, this field commonly contains 12 or + fewer characters. + description: + type: string + description: Description of the payment. + maxLength: 255 + requestId: + description: >- + Request ID of the transaction. This ID must be unique within a 24 + hour period. + + Use this field to prevent duplicated transactions. + type: string + x-sortable: true + hasAmountAdjustment: + description: Specifies if the transaction has amount adjustment. + type: boolean + readOnly: true + gatewayName: + readOnly: true + description: >- + Name of the payment gateway that processed, or is selected to + process, the transaction. + + This value is only available after a gateway is selected for the + transaction. + x-label: Gateway + x-basic: true + allOf: + - $ref: '#/components/schemas/GatewayName' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + processedTime: + description: Date and time when the transaction is processed. + x-sortable: true + x-basic: true + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + gatewayAccountId: + type: + - string + - 'null' + description: ID of the gateway account that processed the transaction. + maxLength: 50 + example: gw_acc_0YVCXMF26DDNKAERE5NW727S34 + readOnly: true + gatewayTransactionId: + description: ID of the gateway transaction. + readOnly: true + type: + - string + - 'null' + maxLength: 50 + example: txn_0YVDTQJ8YWDGQACV2N2N5SPWQ0 + gateway: + type: object + description: Related gateway information. + readOnly: true + properties: + response: + description: Gateway response. + type: object + properties: + code: + description: Gateway response code. + type: + - string + - 'null' + message: + description: Gateway response message. + type: + - string + - 'null' + type: + description: Gateway response type. + type: + - string + - 'null' + originalCode: + description: 'Raw, unmapped gateway response code.' + type: + - string + - 'null' + originalMessage: + description: 'Raw, unmapped gateway response message.' + type: + - string + - 'null' + avsResponse: + description: Gateway Address Verification System (AVS) response. + type: object + properties: + code: + description: Response code. + type: + - string + - 'null' + message: + description: Response message. + type: + - string + - 'null' + originalCode: + description: Raw response code. + type: + - string + - 'null' + originalMessage: + description: Raw response message. + type: + - string + - 'null' + cvvResponse: + description: Gateway Card Verification Value (CVV) response. + type: object + properties: + code: + description: Response code. + type: + - string + - 'null' + message: + description: Response message. + type: + - string + - 'null' + originalCode: + description: Raw response code. + type: + - string + - 'null' + originalMessage: + description: Raw response message. + type: + - string + - 'null' + acquirerName: + readOnly: true + description: >- + Acquirer name. + + This value is only available when a transaction uses a payment + gateway. + + If a transaction does not use a payment gateway, this value is + `null`. + allOf: + - $ref: '#/components/schemas/AcquirerName' + method: + deprecated: true + description: |- + Payment method. + + >**Note:** Use `paymentInstrument.method` instead. + allOf: + - $ref: '#/components/schemas/PaymentMethod' + velocity: + description: Number of transactions by the same customer in the past 24 hours. + type: integer + revision: + description: >- + Number of times the transaction data has been modified. + + + This revision number is useful when analyzing webhook data to + determine if the change takes precedence over the current + representation. + type: integer + readOnly: true + referenceData: + description: Transaction reference data. + type: + - object + - 'null' + additionalProperties: + type: string + example: + gatewayTransactionId: GAT123 + readOnly: true + bin: + description: Payment card Bank Identification Number (BIN). + x-label: BIN + type: + - string + - 'null' + format: bin + readOnly: true + paymentInstrument: + type: object + description: Payment instrument information. + anyOf: + - $ref: '#/components/schemas/VaultedInstrument' + - $ref: '#/components/schemas/AlternativePaymentInstrument' + - $ref: '#/components/schemas/CashInstrument' + - $ref: '#/components/schemas/CheckInstrument' + hasDcc: + description: >- + Specifies if Dynamic Currency Conversion (DCC) applies to the + transaction. + type: boolean + readOnly: true + dcc: + description: |- + Detailed Dynamic currency conversion (DCC). + If DCC is not applied to the transaction, this value is `null`. + type: + - object + - 'null' + readOnly: true + properties: + base: + description: Initial amount and currency to convert from. + $ref: '#/components/schemas/Money' + quote: + description: Suggested amount and currency to convert to. + $ref: '#/components/schemas/Money' + usdMarkup: + description: Markup amount converted to USD. + $ref: '#/components/schemas/MoneyAmount' + outcome: + type: string + description: Dynamic currency conversion outcome. + enum: + - rejected + - selected + - unknown + hasBumpOffer: + description: >- + Specifies if the transaction has a bump offer. + + A bump offer is a discount, purchase bonus, deal, that is offered to + the customer during checkout. + type: boolean + readOnly: true + bumpOffer: + description: >- + Bump offer information. + + If the transaction does not have an associated bump offer, this + value is `null`. + type: + - object + - 'null' + readOnly: true + properties: + order: + description: Initial amount and currency. + $ref: '#/components/schemas/Money' + version: + type: string + description: |- + Name of the version selected. + This field is useful to measure split tests. + language: + description: |- + Language in which the bump offer displays to the user. + This field in useful to find translation issues. + $ref: '#/components/schemas/LanguageIsoCode' + outcome: + type: string + readOnly: true + description: Status of the bump offer. + enum: + - presented + - rejected + - selected + - unknown + presentedOffers: + type: array + readOnly: true + description: Offers presented to a customer. + minItems: 1 + items: + $ref: '#/components/schemas/PurchaseBumpOffer' + selectedOffer: + readOnly: true + description: |- + Offer selected by a customer. + If a bump offer outcome is not `selected`, this value is `null`. + allOf: + - $ref: '#/components/schemas/PurchaseBumpOffer' + riskScore: + description: Risk score for the transaction. + type: integer + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + notificationUrl: + description: >- + URL where a server-to-server POST notification is sent. + + This notification is sent when the transaction result is finalized + after a timeout or an offsite interaction. + + + Do not trust this notification alone, + + complete a GET request to confirm the result of the transaction. + + To ensure the request is not reattempted, + + when the result is confirmed, respond with a 2xx HTTP status code. + + + The following placeholders are available to use in this URI: `{id}` + and `{result}`. + type: + - string + - 'null' + format: uri + isDisputed: + description: Specifies if a transaction is disputed. + type: boolean + readOnly: true + disputeTime: + description: |- + Date and time when the dispute is created. + If the transaction is not disputed, this value is `null`. + type: + - string + - 'null' + format: date-time + readOnly: true + x-sortable: true + disputeStatus: + description: Status of the dispute. + type: + - string + - 'null' + readOnly: true + enum: + - null + - response-needed + - under-review + - forfeited + - won + - lost + - unknown + isReconciled: + description: Specifies if the transaction is verified with gateway batch data. + type: boolean + readOnly: true + isProcessedOutside: + description: Specifies if the transaction is processed outside of Rebilly. + type: boolean + isMerchantInitiated: + description: Specifies if the transaction is initiated by the merchant. + type: boolean + hadDiscrepancy: + description: >- + Specifies if the transaction is updated due to a discrepancy with + its source of truth. + type: boolean + readOnly: true + orderId: + deprecated: true + description: |- + Order ID of the transaction. + This ID must be unique within a 24 hour period. + + > **Note:** Use the `requestId` field instead. + type: string + x-sortable: true + arn: + x-label: ARN + type: + - string + - 'null' + readOnly: true + description: Acquirer reference number. + example: '74836950144358910018150' + reportAmount: + description: >- + Transaction amount converted to the report currency of the + organization. + type: number + x-type: Money + x-sortable: true + x-currency-field: reportCurrency + format: double + readOnly: true + reportCurrency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + settlementTime: + type: + - string + - 'null' + description: >- + Date and time when the transaction is settled by the banking + institution. + format: date-time + readOnly: true + x-sortable: true + discrepancyTime: + type: + - string + - 'null' + description: Date and time of the most recent discrepancy on the transaction. + format: date-time + readOnly: true + x-sortable: true + limits: + $ref: '#/components/schemas/TransactionLimitAmount' + organizationId: + readOnly: true + allOf: + - $ref: '#/components/schemas/OrganizationId' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - attachments + - website + - customer + - gatewayAccount + - paymentCard + - parentTransaction + - leadSource + - approvalUrl + - refundUrl + - updateUrl + - disputes + - invoices + - queryUrl + - redirectUrl + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + parentTransaction: + type: object + childTransactions: + type: array + maxItems: 10 + description: Most recent child transactions. + gatewayAccount: + type: object + customer: + type: object + leadSource: + type: object + website: + type: object + invoices: + type: array + maxItems: 10 + description: Most recent related invoices. + organization: + type: object + dispute: + type: object + paymentCard: + type: object + bankAccount: + type: object + TimeUnit: + type: string + enum: + - second + - minute + - hour + - day + - month + - year + TimePluralUnit: + type: string + enum: + - seconds + - minutes + - hours + - days + - months + - years + TimeIso8601Extended: + description: Extended ISO-8601 format of time. + type: string + format: time + pattern: >- + ^(([01][0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?)((?:[+-](?:0[0-9]|1[12])(?::?[0-5][0-9])?)|Z)?$ + SchedulingMethodDayOfMonth: + type: object + properties: + method: + type: string + enum: + - day-of-month + day: + type: integer + minimum: 1 + maximum: 31 + description: |- + Day of the month in which the event occurs. + If the month has less days, the last day of the month is selected. + time: + $ref: '#/components/schemas/TimeIso8601Extended' + required: + - day + - method + SchedulingMethodDayOfWeek: + type: object + properties: + method: + type: string + enum: + - day-of-week + day: + description: Day of the week when the event occurs. + type: string + enum: + - Sunday + - Monday + - Tuesday + - Wednesday + - Thursday + - Friday + - Saturday + week: + type: string + enum: + - next + - first-in-month + - last-in-month + time: + $ref: '#/components/schemas/TimeIso8601Extended' + required: + - day + - week + - method + PeriodAnchor: + type: object + description: Instruction for calculating the period anchor. + discriminator: + propertyName: method + mapping: + day-of-month: '#/components/schemas/SchedulingMethodDayOfMonth' + day-of-week: '#/components/schemas/SchedulingMethodDayOfWeek' + anyOf: + - $ref: '#/components/schemas/SchedulingMethodDayOfMonth' + - $ref: '#/components/schemas/SchedulingMethodDayOfWeek' + SchedulingMethodDateInterval: + type: object + properties: + method: + type: string + enum: + - date-interval + duration: + type: integer + description: Number of time units. + minimum: 1 + unit: + description: Unit of time. + oneOf: + - $ref: '#/components/schemas/TimeUnit' + - $ref: '#/components/schemas/TimePluralUnit' + anchor: + $ref: '#/components/schemas/PeriodAnchor' + required: + - duration + - unit + - method + SchedulingMethodImmediately: + type: object + required: + - method + properties: + method: + type: string + enum: + - immediately + SchedulingMethodIntelligent: + type: object + properties: + method: + type: string + enum: + - intelligent + duration: + type: integer + description: >- + Latest point in time at which the event should occur. + + The event occurs at a random time between the initial time and + duration time. + minimum: 1 + unit: + description: Unit of time. + oneOf: + - $ref: '#/components/schemas/TimeUnit' + - $ref: '#/components/schemas/TimePluralUnit' + required: + - duration + - unit + - method + InvoiceRetryScheduleInstruction: + type: object + description: Specifies when the payment retry instruction is performed. + discriminator: + propertyName: method + mapping: + date-interval: '#/components/schemas/SchedulingMethodDateInterval' + day-of-month: '#/components/schemas/SchedulingMethodDayOfMonth' + day-of-week: '#/components/schemas/SchedulingMethodDayOfWeek' + immediately: '#/components/schemas/SchedulingMethodImmediately' + intelligent: '#/components/schemas/SchedulingMethodIntelligent' + anyOf: + - $ref: '#/components/schemas/SchedulingMethodDateInterval' + - $ref: '#/components/schemas/SchedulingMethodDayOfMonth' + - $ref: '#/components/schemas/SchedulingMethodDayOfWeek' + - $ref: '#/components/schemas/SchedulingMethodImmediately' + - $ref: '#/components/schemas/SchedulingMethodIntelligent' + AmountAdjustmentPoliciesNone: + title: None + type: object + properties: + method: + type: string + enum: + - none + description: No payment amount adjustment. + AmountAdjustmentPoliciesDiscountAmountRemaining: + title: Discount remaining amount + type: object + properties: + method: + type: string + enum: + - discount-amount-remaining + description: Discount the remaining amount on the invoice. + AmountAdjustmentInstructionPartial: + type: object + required: + - method + - value + - type + properties: + method: + type: string + enum: + - partial + value: + description: Amount of the payment. + type: number + format: float + type: + description: Payment amount type. + type: string + enum: + - percent + - fixed + afterApprovalPolicy: + description: >- + After an approved payment retry for an adjusted amount, this field + specifies whether to discount the remaining invoice amount. + discriminator: + propertyName: method + mapping: + none: '#/components/schemas/AmountAdjustmentPoliciesNone' + discount-amount-remaining: >- + #/components/schemas/AmountAdjustmentPoliciesDiscountAmountRemaining + oneOf: + - $ref: '#/components/schemas/AmountAdjustmentPoliciesNone' + - $ref: >- + #/components/schemas/AmountAdjustmentPoliciesDiscountAmountRemaining + AmountAdjustmentInstructionNone: + type: object + required: + - method + properties: + method: + type: string + enum: + - none + InvoiceRetryAmountAdjustmentInstruction: + type: object + description: Specifies if the payment amount must be adjusted for the retry. + discriminator: + propertyName: method + mapping: + partial: '#/components/schemas/AmountAdjustmentInstructionPartial' + none: '#/components/schemas/AmountAdjustmentInstructionNone' + oneOf: + - $ref: '#/components/schemas/AmountAdjustmentInstructionPartial' + - $ref: '#/components/schemas/AmountAdjustmentInstructionNone' + Invoice: + required: + - customerId + - currency + - websiteId + properties: + id: + type: string + description: ID of the invoice. + readOnly: true + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + websiteId: + $ref: '#/components/schemas/WebsiteId' + invoiceNumber: + description: >- + Auto-incrementing number based on the sequence of invoices for any + particular customer. + readOnly: true + type: integer + x-basic: true + subscriptionId: + type: string + description: |- + ID of the related subscription order, if available. + This field is `null` if there are no related subscription orders. + readOnly: true + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + currency: + x-sortable: true + x-basic: true + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Amount of the invoice. + type: number + x-type: Money + x-sortable: true + x-basic: true + format: double + readOnly: true + amountDue: + description: Amount that is due on the invoice. + type: number + x-type: Money + x-sortable: true + format: double + readOnly: true + subtotalAmount: + description: Subtotal amount of the invoice. + type: number + x-type: Money + format: double + readOnly: true + discountAmount: + description: Discount amount that is applied to the invoice. + type: number + x-type: Money + format: double + readOnly: true + shipping: + $ref: '#/components/schemas/Shipping' + tax: + $ref: '#/components/schemas/Taxes' + organizationTaxIdNumber: + description: Organization tax ID number that is displayed on the invoice. + type: + - object + - 'null' + required: + - type + - value + properties: + type: + type: string + description: Type of the tax ID number. + enum: + - eu-vat + - other + example: eu-vat + value: + type: string + description: Value of the tax ID number. + example: GB980780684 + customerTaxIdNumber: + description: Customer tax ID number that is displayed on the invoice. + type: + - object + - 'null' + required: + - type + - value + properties: + type: + type: string + description: Type of the tax ID number. + enum: + - eu-vat + - other + example: eu-vat + value: + type: string + description: Value of the tax ID number. + example: GB980780684 + billingAddress: + description: Billing address of the invoice. + $ref: '#/components/schemas/ContactObject' + deliveryAddress: + description: Delivery address of the invoice. + $ref: '#/components/schemas/ContactObject' + poNumber: + description: Purchase order number that is displayed on the invoice. + type: + - string + - 'null' + example: PO123456 + maxLength: 50 + notes: + description: Notes for the customer that are displayed on the invoice. + type: string + maxLength: 65535 + items: + type: array + description: Invoice items array. + readOnly: true + items: + $ref: '#/components/schemas/InvoiceItem' + discounts: + type: array + description: Discounts applied. + readOnly: true + items: + type: object + readOnly: true + properties: + couponId: + type: string + description: ID of the coupon. + maxLength: 50 + example: cpn_0YVCNKF81GD778N4YNVGDJK558 + redemptionId: + description: ID of the redemption. + $ref: '#/components/schemas/ResourceId' + amount: + description: Total amount discounted by this coupon. + type: number + format: double + description: + type: string + description: Description of the discount. + context: + $ref: '#/components/schemas/DiscountContext' + autopayScheduledTime: + description: Date and time when an automatic payment (autopay) is scheduled. + type: + - string + - 'null' + x-sortable: true + format: date-time + autopayRetryNumber: + description: >- + Number of times that an automatic payment (autopay) has been + attempted on an invoice. + readOnly: true + type: integer + x-sortable: true + minimum: 0 + default: 0 + status: + type: string + description: Status of the invoice. + x-basic: true + readOnly: true + enum: + - draft + - unpaid + - paid + - partially-paid + - past-due + - abandoned + - voided + - partially-refunded + - refunded + - disputed + delinquentCollectionPeriod: + type: integer + description: >- + Length of time, in days, between when the invoice is due and when + the invoice is paid. + x-sortable: true + readOnly: true + collectionPeriod: + type: integer + x-sortable: true + description: >- + Length of time, in days, between when the invoice is issued and when + the invoice is paid. + readOnly: true + abandonedTime: + description: Date and time when the invoice is abandoned. + x-sortable: true + type: + - string + - 'null' + format: date-time + readOnly: true + voidedTime: + description: Date and time when the invoice is voided. + x-sortable: true + type: + - string + - 'null' + format: date-time + readOnly: true + paidTime: + x-label: Payment Date + x-sortable: true + x-basic: true + description: Date and time when the invoice is paid. + type: + - string + - 'null' + format: date-time + readOnly: true + dueTime: + description: Date and time when the invoice is due for payment. + type: string + x-sortable: true + format: date-time + issuedTime: + description: Date and time when the invoice is issued. + x-label: Date Issued + x-sortable: true + x-basic: true + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + paymentFormUrl: + type: + - string + - 'null' + readOnly: true + format: url + description: |- + URL where the customer is redirected to pay the invoice + using one of the methods which are available to the customer. + This is an alternative to creating a new transaction with empty + `methods`. + customerId: + x-basic: true + allOf: + - $ref: '#/components/schemas/CustomerId' + transactions: + type: array + description: Invoice transactions array. + maxItems: 10 + readOnly: true + items: + $ref: '#/components/schemas/Transaction' + retryInstruction: + description: >- + Invoice payment retry instruction. + + This object specifies how to proceed if a payment related to the + invoice fails. + type: + - object + - 'null' + properties: + attempts: + type: array + description: Describes the retry instruction. + minItems: 1 + items: + type: object + properties: + scheduleInstruction: + $ref: '#/components/schemas/InvoiceRetryScheduleInstruction' + amountAdjustmentInstruction: + $ref: >- + #/components/schemas/InvoiceRetryAmountAdjustmentInstruction + tryBackupInstruments: + description: >- + Specifies whether to use backup payment instruments on an + invoice payment retry. + type: boolean + default: false + required: + - scheduleInstruction + afterAttemptPolicies: + description: >- + Describes the action to take when a payment attempt concludes, + and payment is not collected. + type: array + items: + type: string + enum: + - change-subscription-renewal-time + afterRetryEndPolicies: + description: >- + Describes the action to take when all scheduled payment retries, + in a retry instruction, have concluded and payment is not + collected. + type: array + items: + type: string + enum: + - abandon-invoice + - cancel-subscription + required: + - attempts + - afterAttemptPolicies + - afterRetryEndPolicies + revision: + description: |- + Number of times the invoice data has been modified. + + Use the revision number when analyzing webhook data to + determine if a change should take precedence over the current + representation. + type: integer + readOnly: true + type: + description: Type of invoice. + type: string + enum: + - initial + - renewal + - interim + - cancellation + - one-time + - refund + - charge + - one-time-sale + readOnly: true + dueReminderTime: + description: Date and time when a past due reminder event is triggered. + type: + - string + - 'null' + format: date-time + readOnly: true + dueReminderNumber: + description: Number of past due reminder events that have been triggered. + type: + - integer + - 'null' + readOnly: true + organizationId: + readOnly: true + allOf: + - $ref: '#/components/schemas/OrganizationId' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - website + - customer + - organization + - attachments + - leadSource + - transactionAllocations + - recalculateInvoice + - subscription + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + website: + type: object + organization: + type: object + leadSource: + type: object + shippingRate: + type: object + InvoiceIssue: + type: object + properties: + issuedTime: + description: >- + Date and time when the invoice is issued. + + If this field is `null` or omitted, the invoice is issued + immediately. + type: + - string + - 'null' + format: date-time + dueTime: + description: >- + Date and time when the invoice is due for payment. + + If this field is `null` or omitted, this value is set to the + `issuedTime` value. + type: + - string + - 'null' + format: date-time + InvoiceReissue: + type: object + properties: + dueTime: + description: >- + Date and time when the invoice is due for payment. + + If this field is `null` or omitted, this value is set to the current + date-time. + type: + - string + - 'null' + format: date-time + InvoiceTransactionAllocation: + type: object + properties: + invoiceId: + type: string + description: Unique resource ID. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + transactionId: + $ref: '#/components/schemas/TransactionId' + amount: + type: number + format: double + currency: + $ref: '#/components/schemas/CurrencyCode' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - invoice + - transaction + InvoiceTransaction: + type: object + required: + - transactionId + properties: + transactionId: + description: ID of the transaction to apply to the invoice. + $ref: '#/components/schemas/TransactionId' + amount: + description: >- + Amount to be applied to the invoice. This value must not exceed the + transaction amount. + + If omitted, the lesser of the unused transaction amount or the + invoice due amount is used. + type: number + format: double + InvoiceTimeline: + type: object + properties: + id: + type: string + description: ID of the timeline message. + readOnly: true + maxLength: 50 + example: tmln_0YV8Q9WEF5DTA8HFXS27D1G6GC + type: + description: Type of timeline message. + type: string + readOnly: true + enum: + - coupon-applied + - email-message-sent + - invoice-abandoned + - invoice-disputed + - invoice-issued + - invoice-paid + - invoice-partially-paid + - invoice-partially-refunded + - invoice-past-due + - invoice-refunded + - invoice-reissued + - invoice-renewal-payment-declined + - invoice-revenue-recognized + - invoice-tax-calculation-failed + - invoice-voided + - quickbooks-credit-memo-created + - quickbooks-credit-memo-voided + - quickbooks-invoice-created + - quickbooks-invoice-task-failed + - quickbooks-invoice-updated + - quickbooks-invoice-voided + - quickbooks-revenue-recognition-created + - timeline-comment-created + - transaction-abandoned + - transaction-approved + - transaction-canceled + - transaction-declined + - transaction-initiated + - transaction-refunded + - transaction-voided + triggeredBy: + description: 'Specifies who, or what, triggered the timeline event.' + type: string + readOnly: true + enum: + - rebilly + - app + - direct-api + message: + description: Describes the message details. + type: string + extraData: + $ref: '#/components/schemas/TimelineExtraData' + occurredTime: + description: Date and time when the timeline message occurred. + readOnly: true + $ref: '#/components/schemas/ServerTimestamp' + _links: + $ref: '#/components/schemas/SelfLink' + CreditMemoTaxItem: + type: object + required: + - amount + - description + properties: + amount: + description: Amount of the tax. + type: number + format: double + description: + type: string + description: Description of the tax. + rate: + description: >- + Overall sales tax rate which includes state, county, city and + district tax. + type: + - number + - 'null' + format: double + stateAmount: + description: Amount of sales tax to collect for the state. + type: + - number + - 'null' + format: double + example: 0.94 + countyAmount: + description: Amount of sales tax to collect for the county. + type: + - number + - 'null' + format: double + example: 0.04 + cityAmount: + description: Amount of sales tax to collect for the city. + type: + - number + - 'null' + format: double + example: 0 + specialDistrictAmount: + description: Amount of sales tax to collect for the special district. + type: + - number + - 'null' + format: double + example: 0.38 + stateRate: + description: State sales tax rate for given location. + type: + - number + - 'null' + format: double + countyRate: + description: County sales tax rate for given location. + type: + - number + - 'null' + format: double + cityRate: + description: City sales tax rate for given location. + type: + - number + - 'null' + format: double + specialDistrictRate: + description: Special district sales tax rate for given location. + type: + - number + - 'null' + format: double + jurisdictions: + description: Jurisdiction names for the invoice. + type: + - object + - 'null' + properties: + country: + description: Two-letter ISO country code for the provided location. + type: + - string + - 'null' + example: US + state: + description: Postal abbreviated state name for the provided location. + type: + - string + - 'null' + example: CA + county: + description: County name for the provided location. + type: + - string + - 'null' + example: LOS ANGELES + city: + description: City name for the provided location. + type: + - string + - 'null' + example: LOS ANGELES + CreditMemo: + type: object + description: Credit memo object. + required: + - customerId + - currency + properties: + id: + type: string + description: ID of the credit memo. + readOnly: true + maxLength: 50 + example: crmm_0YVCNN22TWC3G8H82QNPNVZCHG + number: + description: >- + Auto-incrementing number based on the sequence of credit memos for + any particular customer. + readOnly: true + type: integer + allocations: + type: object + description: Allocations reduce the unused amount of a credit memo. + properties: + transactions: + type: array + description: >- + List of transactions (typically refunds, credits, and + chargebacks) that are allocated to reduce the unused amount of a + credit memo. + + - To delete transaction allocations, send an empty transaction allocation array in the request. + - To modify transaction allocations, send a modified transaction allocation array in the request. + - If you do not want to modify or delete transaction allocations, do not send a transaction allocation array in the request. + items: + type: object + properties: + transactionId: + description: >- + ID of the transaction to which the credit memo is + allocated. + $ref: '#/components/schemas/TransactionId' + amount: + description: >- + Amount of credit that is allocated from the credit memo to + the transaction. + + If the `amount` value is not supplied or exceeds the + unused amount of the credit memo or the transaction + amount, the lesser of the following two values is used: + + - The unused amount of the credit memo. + + - The transaction amount. + type: number + format: double + currency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + invoices: + type: array + description: >- + List of invoices that the credit memo is allocated to. + + + - To delete invoice allocations, send an empty invoice + allocation array in the request. + Only `unpaid`, `partially-paid`, and `past-due` invoices can be deleted. + - To modify invoice allocations, send a modified invoice + allocation array in the request. + Only `unpaid`, `partially-paid`, and `past-due` invoices can be modified. + - If you do not want to modify or delete invoice allocations, do + not send an invoice allocation array in the request. + items: + type: object + properties: + invoiceId: + type: string + description: ID of invoice to which the credit memo is allocated. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + amount: + description: >- + Amount of credit that is allocated from the credit memo to + the invoice. + + If the `amount` value is not supplied or exceeds the + unused amount of the credit memo or the invoice due + amount, the lesser of the following two values is used: + + - The unused amount of the credit memo. + + - The invoice due amount. + type: number + format: double + currency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + createdTime: + description: Date and time at which a credit memo is allocated. + $ref: '#/components/schemas/ServerTimestamp' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + items: + type: array + description: Items of the credit memo. + items: + type: object + required: + - unitPrice + - quantity + properties: + id: + type: string + description: ID of the credit memo item. + readOnly: true + maxLength: 50 + example: crmm_itm_0YVCNN22TWC3G8H82QNPNVZCHG + invoiceItemId: + description: ID of the invoice item to which the credit item is referenced. + type: + - string + - 'null' + maxLength: 50 + example: ii_0YVFDEQS2KCFTBN9HXWJFY55GV + description: + description: Description of the credit memo item. + type: string + unitPrice: + description: Price of the credit memo item. + type: number + format: double + quantity: + description: Quantity of the credit memo item. + type: integer + price: + description: Total price of the credit memo item. + type: number + format: double + readOnly: true + productId: + type: + - string + - 'null' + description: ID of the related product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + planId: + description: ID of the related plan. + type: + - string + - 'null' + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + tax: + description: Credit memo item tax. + oneOf: + - $ref: '#/components/schemas/CreditMemoTaxItem' + - type: 'null' + status: + type: string + description: Status of the credit memo. + readOnly: true + enum: + - issued + - applied + - partially-applied + - voided + x-enumDescriptions: + issued: The credit memo is available with a full amount to be applied. + applied: >- + The full amount of the credit memo has been applied. No more + credit can be created from it. + partially-applied: |- + A partial amount of the credit memo has been applied. + A credit can be created from the remaining amount. + voided: Credit memo has been voided and cannot be used anymore. + reason: + description: Reason for the credit memo. + type: string + enum: + - return + - product-unsatisfactory + - order-change + - order-cancellation + - chargeback + - write-off + - waiver + - customer-credit + - other + description: + type: string + x-basic: true + description: >- + Public description, that is visible to customers, which describes + the purpose of the credit memo. + shippingAmount: + description: Shipping amount of an invoice to credit. + type: number + format: double + default: 0 + x-type: Money + taxAmount: + description: Sum of items tax amount of an invoice to credit. + readOnly: true + type: number + format: double + default: 0 + x-type: Money + totalAmount: + description: >- + Total amount of all credits in the credit memo, including items, + shipping, and tax. + readOnly: true + type: number + format: double + default: 0 + x-type: Money + unusedAmount: + description: Unused credit memo amount that has not been allocated. + readOnly: true + type: number + format: double + default: 0 + x-type: Money + revision: + type: integer + readOnly: true + description: Number of times the credit memo has been modified. + customerId: + readOnly: true + $ref: '#/components/schemas/CustomerId' + currency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + invoiceId: + type: + - string + - 'null' + description: ID of the invoice to which the credit memo is issued. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + - invoice + PatchCreditMemo: + type: object + description: Patch credit memo object. + properties: + allocations: + type: object + description: Allocations reduce the unused amount of a credit memo. + properties: + transactions: + type: array + description: >- + List of transactions (typically refunds, credits, and + chargebacks) that are allocated to reduce the unused amount of a + credit memo. + + - To delete transaction allocations, send an empty transaction allocation array in the request. + - To modify transaction allocations, send a modified transaction allocation array in the request. + - If you do not want to modify or delete transaction allocations, do not send a transaction allocation array in the request. + items: + type: object + properties: + transactionId: + description: >- + ID of the transaction to which the credit memo is + allocated. + $ref: '#/components/schemas/TransactionId' + amount: + description: >- + Amount of credit that is allocated from the credit memo to + the transaction. + + If the `amount` value is not supplied or exceeds the + unused amount of the credit memo or the transaction + amount, the lesser of the following two values is used: + + - The unused amount of the credit memo. + + - The transaction amount. + type: number + format: double + currency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + invoices: + type: array + description: >- + List of invoices that the credit memo is allocated to. + + + - To delete invoice allocations, send an empty invoice + allocation array in the request. + Only `unpaid`, `partially-paid`, and `past-due` invoices can be deleted. + - To modify invoice allocations, send a modified invoice + allocation array in the request. + Only `unpaid`, `partially-paid`, and `past-due` invoices can be modified. + - If you do not want to modify or delete invoice allocations, do + not send an invoice allocation array in the request. + items: + type: object + properties: + invoiceId: + type: string + description: ID of invoice to which the credit memo is allocated. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + amount: + description: >- + Amount of credit that is allocated from the credit memo to + the invoice. + + If the `amount` value is not supplied or exceeds the + unused amount of the credit memo or the invoice due + amount, the lesser of the following two values is used: + + - The unused amount of the credit memo. + + - The invoice due amount. + type: number + format: double + currency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + createdTime: + description: Date and time at which a credit memo is allocated. + $ref: '#/components/schemas/ServerTimestamp' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + items: + type: array + description: Items of the credit memo. + items: + type: object + required: + - unitPrice + - quantity + properties: + id: + type: string + description: ID of the credit memo item. + readOnly: true + maxLength: 50 + example: crmm_itm_0YVCNN22TWC3G8H82QNPNVZCHG + invoiceItemId: + description: ID of the invoice item to which the credit item is referenced. + type: + - string + - 'null' + maxLength: 50 + example: ii_0YVFDEQS2KCFTBN9HXWJFY55GV + description: + description: Description of the credit memo item. + type: string + unitPrice: + description: Price of the credit memo item. + type: number + format: double + quantity: + description: Quantity of the credit memo item. + type: integer + price: + description: Total price of the credit memo item. + type: number + format: double + readOnly: true + productId: + type: + - string + - 'null' + description: ID of the related product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + planId: + description: ID of the related plan. + type: + - string + - 'null' + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + tax: + description: Credit memo item tax. + oneOf: + - $ref: '#/components/schemas/CreditMemoTaxItem' + - type: 'null' + reason: + description: Reason for the credit memo. + type: string + enum: + - return + - product-unsatisfactory + - order-change + - order-cancellation + - chargeback + - write-off + - waiver + - customer-credit + - other + description: + type: string + x-basic: true + description: >- + Public description, that is visible to customers, which describes + the purpose of the credit memo. + shippingAmount: + description: Shipping amount of an invoice to credit. + type: number + format: double + default: 0 + x-type: Money + CreditMemoTimeline: + type: object + properties: + id: + type: string + description: ID of the timeline message. + readOnly: true + maxLength: 50 + example: tmln_0YV8Q9WEF5DTA8HFXS27D1G6GC + type: + description: Type of timeline message. + type: string + readOnly: true + enum: + - credit-memo-created + - credit-memo-applied + - credit-memo-partially-applied + - credit-memo-voided + triggeredBy: + description: 'Specifies who, or what, triggered the timeline event.' + type: string + readOnly: true + enum: + - rebilly + - app + - direct-api + message: + description: Content of the timeline message. + type: string + extraData: + $ref: '#/components/schemas/TimelineExtraData' + occurredTime: + description: Date and time when the timeline message occurred. + readOnly: true + $ref: '#/components/schemas/ServerTimestamp' + _links: + $ref: '#/components/schemas/SelfLink' + JournalAccount: + type: object + required: + - name + properties: + id: + type: string + description: ID of the journal account. + readOnly: true + maxLength: 50 + example: jrn_acc_0YVCXS791DD8JAK1WV3VHM70ZQ + name: + type: string + description: Name of the journal account. + example: Unearned revenue + description: + type: + - string + - 'null' + createdTime: + description: Date and time when the journal record is created. + $ref: '#/components/schemas/ServerTimestamp' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + JournalEntry: + type: object + required: + - period + - currency + properties: + id: + type: string + description: ID of the journal entry. + readOnly: true + maxLength: 50 + example: jrn_ent_0YVCXSFCF2DJX99NBBSJCEVETB + period: + type: object + properties: + startDate: + type: string + format: date + example: '2022-09-01' + endDate: + type: string + format: date + example: '2022-09-30' + currency: + description: Currency of the journal record revenue. + $ref: '#/components/schemas/CurrencyCode' + description: + type: string + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + JournalRecord: + type: object + required: + - type + - journalEntryId + - customerId + - invoiceId + - invoiceItemId + - debitAccountId + - creditAccountId + properties: + id: + type: string + description: Unique resource ID. + maxLength: 50 + example: jrn_rec_0YVCXV2HE5DBT89QV7RXSJEZQ4 + journalEntryId: + description: ID of the journal entry. + readOnly: true + type: string + customerId: + description: ID of the customer. + type: string + maxLength: 50 + example: cus_0YV7DDSDD1C8DA64KHH2W33CPF + invoiceId: + type: string + description: ID of the invoice. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + invoiceItemId: + type: string + description: ID of the invoice item. + maxLength: 50 + example: ii_0YVFDEQS2KCFTBN9HXWJFY55GV + type: + type: string + readOnly: true + enum: + - automated + - manual + estimatedAmount: + description: >- + Amount of revenue estimated to be recognized at the schedule date. + + This value is ignored when updating a journal record with a `type` + of `automated`. + type: + - number + - 'null' + format: double + recognizedAmount: + description: Amount of revenue recognized at the journal period end. + type: + - number + - 'null' + format: double + debitAccountId: + description: ID of the debit journal account. + type: + - string + - 'null' + creditAccountId: + description: ID of the credit journal account. + type: + - string + - 'null' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + invoice: + type: object + invoiceItem: + type: object + debitAccount: + type: object + creditAccount: + type: object + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + - invoice + - invoiceItem + - journalEntry + - creditAccount + - debitAccount + KycDocumentTypes: + type: string + enum: + - identity-proof + - address-proof + - funds-proof + - purchase-proof + - credit-file-proof + KycDocumentSubtypes: + type: + - string + - 'null' + enum: + - passport + - id-card + - driver-license + - birth-certificate + - utility-bill + - rental-receipt + - lease-agreement + - copy-credit-card + - credit-card-statement + - bank-statement + - inheritance-documentation + - tax-return + - salary-slip + - sale-of-assets + - public-health-card + - proof-of-age-card + - reverse-of-id + - public-service + - ewallet-holder-details + - ewallet-transaction-statement + - marriage-certificate + - firearms-license + - insurance-letter + - income-statement + - debtors-letter + - other + - null + KycRequestDocument: + type: object + required: + - type + properties: + type: + description: Type of document to request from the customer. + $ref: '#/components/schemas/KycDocumentTypes' + subtypes: + description: Permitted document subtype. + type: + - array + - 'null' + items: + $ref: '#/components/schemas/KycDocumentSubtypes' + maxAttempts: + description: |- + Total number of allowed document upload attempts. + Use `0` to allow unlimited upload attempts. + type: integer + default: 3 + minimum: 0 + maximum: 100 + faceProofRequired: + description: >- + Specifies if the customer must upload a photo of their face (selfie) + that matches a provided KYC document. + type: boolean + KycRequest: + type: object + description: KYC request information. + required: + - customerId + - documents + properties: + id: + type: string + readOnly: true + description: ID of the KYC request. + maxLength: 50 + example: kyc_req_0YV7JMJ3DBCGRBR7K9D4HVGPP5 + customerId: + $ref: '#/components/schemas/CustomerId' + documents: + type: array + description: Documents to request from the customer. + minItems: 1 + items: + $ref: '#/components/schemas/KycRequestDocument' + status: + description: Status of the request. + type: string + readOnly: true + enum: + - gathering + - attempted + - partial + - pending-review + - fulfilled + - failed + - abandoned + - expired + x-enumDescriptions: + gathering: |- + No documents have been provided yet. + This is a temporary state. + attempted: >- + At least one document has been provided but none were assigned the + `accepted` status. + + This is a temporary state. + partial: >- + At least one requested document has the `accepted` status, + + but not all requested documents have been assigned the `accepted` + status. + + This is a temporary state. + pending-review: >- + At least one requested document has the `pending` status, + + and no requested documents have been assigned the `accepted` + status. + + This is a temporary state, until the document is reviewed, + + or another `accepted` document is provided. + fulfilled: >- + All requested documents are provided and have been assigned the + `accepted` status. + + This is a permanent state. + failed: >- + At least one requested document has exhausted all attempts, + + and has not been assigned a `accepted`, `pending`, or + `in-progress` status. + + This is a permanent state. + abandoned: |- + One or more documents provided but the request expired. + This is a permanent state. + expired: |- + No documents were provided and the request expired. + This is a permanent state. + redirectUrl: + description: >- + URL where the customer is redirected when a KYC document upload is + complete. + + When the customer is redirected, + + Rebilly appends an `info` query parameter that has one of the + following values: + - `back`: Customer clicked the `back to website` link. + - `token_expired`: Customer's token expired. + - `success`: Customer uploaded KYC documents that have been analyzed. + - `manual`: Customer uploaded KYC documents that require a manual review. + This is because the analyzer rejected the documents or could not process them. + - `partial`: Some of the customer's KYC documents have been analyzed, + but other documents have not. + For example, this may occur when a proof of address document is analyzed but proof of ID is not. + + Example: `https://example.com?info=success`. + type: + - string + - 'null' + format: uri + reason: + description: Reason for uploading. + example: spend limit + type: + - string + - 'null' + matchLevel: + description: |- + Tolerance level setting for document matches. + The value of `1` is more tolerant and `2` is strict. + type: integer + minimum: 1 + maximum: 2 + default: 2 + revision: + description: >- + Number of times the KYC request data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + expirationTime: + description: |- + Date and time when the request expires. + The default value is one hour in the future. + type: string + format: date-time + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - documents + - gatherer + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + documents: + type: array + KycDocumentRejectionReasonTypes: + description: Reason the document is rejected. + type: string + enum: + - document-unreadable + - document-expired + - document-not-matching + - document-duplicate + - document-invalid + - document-not-open + - underage-person + - third-party-or-mismatch + - expiration-date-missing + - issue-date-missing + - dob-mismatch + - name-mismatch + - critical-info-missing + - old-address-on-id + - tampered-document + - other + KycDocumentRejection: + description: Reason the document is rejected. + type: + - object + - 'null' + readOnly: true + properties: + type: + $ref: '#/components/schemas/KycDocumentRejectionReasonTypes' + message: + description: KYC document rejection message. + type: string + example: Provided document is unreadable + KycIdentityMatches: + type: object + description: Matched identity data. + properties: + containsImage: + description: Specifies if the document includes an image that contains a face. + type: boolean + example: true + isIdentityDocument: + description: Specifies if the document resembles an ID. + type: boolean + example: true + isPublishedOnline: + description: Specifies if an exact match of the document has been found online. + type: boolean + example: false + matchingImages: + description: URLs where matching images have been found online. + type: array + readOnly: true + maxItems: 3 + items: + type: string + firstName: + description: |- + First name of the customer. + This value is null if no match is found. + type: + - string + - 'null' + example: John + lastName: + description: |- + Last name of the customer. + This value is null if no match is found. + type: + - string + - 'null' + example: Doe + dateOfBirth: + description: |- + Date of birth detected on the document. + This value is null if no match is detected. + type: + - string + - 'null' + format: date-time + expirationDate: + description: |- + Expiration date detected on the document. + This value is null if no expiration date is detected. + type: + - string + - 'null' + format: date-time + issueDate: + description: |- + Issue date detected on the document. + This value is null if no issue date is detected. + type: + - string + - 'null' + format: date-time + hasMinimalAge: + description: |- + Specifies that the individual is older than the minimal age limit. + The minimal age is 21+ the for USA and 18+ for all other countries. + This value is null if `dateOfBirth` is not determined. + type: + - boolean + - 'null' + readOnly: true + example: true + nationality: + description: |- + Nationality detected on a passport or citizenship document. + This value is null if no nationality is detected. + type: + - string + - 'null' + example: US + maxLength: 3 + issuanceCountry: + description: Country that issued the document. + type: + - string + - 'null' + example: CA + minLength: 2 + maxLength: 2 + issuanceRegion: + description: 'Region, state, province, or territory that issued the document.' + type: + - string + - 'null' + example: Ontario + documentNumber: + description: |- + Unique number on the identity document. + This value may contain alphanumeric characters. + type: + - string + - 'null' + example: '1234567890' + documentSubtype: + description: Interpreted subtype of the uploaded document. + type: + - string + - 'null' + $ref: '#/components/schemas/KycDocumentSubtypes' + hasMatchingFaceProof: + description: Specifies if an identity document has matching face proof. + type: boolean + example: false + isTampered: + description: Specifies if an identity document has been tampered with. + type: boolean + example: false + expiryDate: + description: Use `expirationDate` field instead. + type: + - string + - 'null' + format: date-time + deprecated: true + KycSettingsIdentity: + type: object + description: Identity proof settings. + properties: + weights: + type: object + description: >- + Property weights that are used for the KYC document verification + process. + + + All KYC documents start the verification process with a score of + 100. + + If a check fails, the score is reduced by the corresponding weight. + + For example, if the `firstName` check weight is set to `5`, and the + check fails, + + the KYC document score becomes `95`. + properties: + containsImage: + type: integer + minimum: 0 + maximum: 100 + description: >- + Weight added if the document does not include an image that + contains a face. + isIdentityDocument: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if the document does not resemble an ID. + isPublishedOnline: + type: integer + minimum: 0 + maximum: 100 + description: >- + Weight added if an exact match of the document is not found + online. + firstName: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if the customer's first name is not matched. + lastName: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if the customer's last name is not matched. + expirationDate: + type: integer + minimum: 0 + maximum: 100 + description: >- + Weight added if an expiration date is not detected on the + document. + dateOfBirth: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if a date of birth is not detected on the document. + matchesDateOfBirth: + type: integer + minimum: 0 + maximum: 100 + description: >- + Weight added if an identity document does not have a matching + date of birth. + issueDate: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if an issue date is not detected on the document. + hasMinimalAge: + type: integer + minimum: 0 + maximum: 100 + description: >- + Weight added if the document does not verify the minimal age + limit. + + Minimal age is 21+ the for USA and 18+ for all other countries. + hasMatchingFaceProof: + type: integer + minimum: 0 + maximum: 100 + description: >- + Weight added if an identity document does not have matching face + proof. + nationality: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if a nationality is not detected on the document. + documentSubtype: + type: integer + minimum: 0 + maximum: 100 + description: >- + Weight added if the document is not one of the recognized + document subtypes. + isTampered: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if the document has been tampered with. + thresholds: + type: object + description: >- + Pass and fail threshold definition for the document verification + process. + properties: + rejectBelow: + type: integer + minimum: 0 + maximum: 100 + description: >- + Overall score by which an identity proof document fails the + verification process. + acceptAbove: + type: integer + minimum: 0 + maximum: 100 + description: >- + Overall score by which an identity proof document passes the + verification process. + ProofOfIdentityKycDocument: + title: KYC documents. + type: object + required: + - customerId + - documentType + - status + - fileIds + properties: + id: + type: string + readOnly: true + description: ID of the KYC document. + maxLength: 50 + example: kyc_doc_0YV7JHY705C6DA487BFTAA33V8 + fileIds: + description: >- + IDs of linked file objects. + + + Uploaded `identity-proof` files must have the following tags + attached to be used for KYC purposes: + + `['kyc', 'id-front']`, `['kyc', 'id-back']`, `['kyc', + 'face-proof']`. + type: array + items: + type: string + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + documentType: + description: >- + Document type submitted for validation. + + Only the `identity-proof` and `address-proof` types are analyzed + automatically. + $ref: '#/components/schemas/KycDocumentTypes' + documentSubtype: + description: Document subtype submitted for validation. + $ref: '#/components/schemas/KycDocumentSubtypes' + status: + description: Status of the validation. + type: string + readOnly: true + enum: + - pending + - in-progress + - accepted + - rejected + - archived + x-enumDescriptions: + pending: Waiting to be reviewed or analyzed. + in-progress: Being analyzed by the Rebilly AI. + accepted: Accepted by AI or a human. + rejected: Rejected by AI or a human. + archived: Archived by the Rebilly AI. + rejectionReason: + $ref: '#/components/schemas/KycDocumentRejection' + requestId: + readOnly: true + type: + - string + - 'null' + description: ID of the KYC request. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + processedTime: + description: Date and time when the KYC document is processed. + type: + - string + - 'null' + format: date-time + readOnly: true + customerId: + $ref: '#/components/schemas/CustomerId' + reviewerId: + description: ID of the KYC document reviewer. + type: + - string + - 'null' + readOnly: true + maxLength: 50 + example: 44433322-2c4y-483z-a0a9-158621f77a21 + reviewerName: + description: First and last name of the KYC document reviewer. + type: + - string + - 'null' + readOnly: true + reviewStartTime: + description: Date and time when the manual review starts. + type: + - string + - 'null' + format: date-time + readOnly: true + reviewTime: + description: Date and time of manual review. + type: + - string + - 'null' + format: date-time + readOnly: true + notes: + description: Reviewer notes. + type: + - string + - 'null' + tags: + description: List of KYC document tags. + readOnly: true + type: array + items: + $ref: '#/components/schemas/Tag' + reason: + description: Reason for uploading. + type: + - string + - 'null' + matchLevel: + description: Tolerance level setting for document matches. + type: integer + minimum: 1 + maximum: 2 + example: 2 + revision: + description: >- + Number of times the KYC document data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + documentMatches: + type: + - object + - 'null' + readOnly: true + description: Proof of identity document matches. + properties: + score: + description: >- + Calculated score that represents the percentage of confidence + that this ID represents the customer. + type: number + format: double + example: 0.75 + data: + $ref: '#/components/schemas/KycIdentityMatches' + parsedData: + type: + - object + - 'null' + readOnly: true + description: Parsed data. + properties: + score: + description: >- + Calculated score that represents the percentage of confidence + that this ID represents the customer. + type: number + format: double + example: 0.75 + data: + $ref: '#/components/schemas/KycIdentityMatches' + settings: + description: Settings used to score the document. + readOnly: true + oneOf: + - $ref: '#/components/schemas/KycSettingsIdentity' + - type: 'null' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + files: + type: array + maxItems: 3 + description: Linked files. + KycAddressMatches: + type: object + description: Matched address data. + properties: + firstName: + description: |- + First name of the customer. + This value is null if no match is found. + type: + - string + - 'null' + example: John + lastName: + description: |- + Last name of the customer. + This value is null if no match is found. + type: + - string + - 'null' + example: Doe + line1: + description: |- + Address of the customer's residence. + This value is null if no match is found. + type: + - string + - 'null' + example: 36 Craven St + city: + description: |- + Customer's city of residence. + This value is null if no match is found. + type: + - string + - 'null' + example: London + region: + description: |- + Customer's region of residence. + This value is null if no match is found. + type: + - string + - 'null' + example: London + postalCode: + description: |- + Postal code of the customer's residence. + This value is null if no match is found. + type: + - string + - 'null' + example: WC2N 5NF + wordCount: + description: Total number of words in the document. + type: integer + example: 350 + uniqueWords: + description: Total number of unique words in the document. + type: integer + example: 175 + date: + description: |- + Date detected on the document. + Use this field to determine if the document is recent. + type: + - string + - 'null' + format: date + example: '2021-01-01' + phone: + description: Phone number of the company or agency that issued the document. + type: + - string + - 'null' + example: (123) 456-7890 + documentSubtype: + description: Interpreted subtype of the uploaded document. + type: + - string + - 'null' + $ref: '#/components/schemas/KycDocumentSubtypes' + isTampered: + description: Specifies if an address proof document has been tampered with. + type: boolean + example: false + KycSettingsAddress: + type: object + description: Address proof settings. + properties: + weights: + type: object + description: >- + Property weights that are used for the KYC document verification + process. + + + All KYC documents start the verification process with a score of + 100. + + If a check fails, the score is reduced by the corresponding weight. + + For example, if the `firstName` check weight is set to `5`, and the + check fails, + + the KYC document score becomes `95`. + properties: + firstName: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if the customer's first name is not matched. + lastName: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if the customer's last name is not matched. + line1: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if the customer's address is not matched. + city: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if the customer's city is not matched. + region: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if the customer's region is not matched. + postalCode: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if the customer's postal code is not matched. + date: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if a date is not detected on the document. + phone: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if a phone number is not detected on the document. + documentSubtype: + type: integer + minimum: 0 + maximum: 100 + description: >- + Weight added if the document is not one of the recognized + document subtypes. + isTampered: + type: integer + minimum: 0 + maximum: 100 + description: Weight added if the document has been tampered with. + thresholds: + type: object + description: >- + Pass and fail threshold definition for the document verification + process. + properties: + rejectBelow: + type: integer + minimum: 0 + maximum: 100 + description: >- + Overall score by which an identity proof document fails the + verification process. + acceptAbove: + type: integer + minimum: 0 + maximum: 100 + description: >- + Overall score by which an identity proof document passes the + verification process. + ProofOfAddressKycDocument: + title: KYC documents. + type: object + required: + - customerId + - documentType + - status + - fileIds + properties: + id: + type: string + readOnly: true + description: ID of the KYC document. + maxLength: 50 + example: kyc_doc_0YV7JHY705C6DA487BFTAA33V8 + fileIds: + description: >- + IDs of linked file objects. + + + Uploaded `identity-proof` files must have the following tags + attached to be used for KYC purposes: + + `['kyc', 'id-front']`, `['kyc', 'id-back']`, `['kyc', + 'face-proof']`. + type: array + items: + type: string + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + documentType: + description: >- + Document type submitted for validation. + + Only the `identity-proof` and `address-proof` types are analyzed + automatically. + $ref: '#/components/schemas/KycDocumentTypes' + documentSubtype: + description: Document subtype submitted for validation. + $ref: '#/components/schemas/KycDocumentSubtypes' + status: + description: Status of the validation. + type: string + readOnly: true + enum: + - pending + - in-progress + - accepted + - rejected + - archived + x-enumDescriptions: + pending: Waiting to be reviewed or analyzed. + in-progress: Being analyzed by the Rebilly AI. + accepted: Accepted by AI or a human. + rejected: Rejected by AI or a human. + archived: Archived by the Rebilly AI. + rejectionReason: + $ref: '#/components/schemas/KycDocumentRejection' + requestId: + readOnly: true + type: + - string + - 'null' + description: ID of the KYC request. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + processedTime: + description: Date and time when the KYC document is processed. + type: + - string + - 'null' + format: date-time + readOnly: true + customerId: + $ref: '#/components/schemas/CustomerId' + reviewerId: + description: ID of the KYC document reviewer. + type: + - string + - 'null' + readOnly: true + maxLength: 50 + example: 44433322-2c4y-483z-a0a9-158621f77a21 + reviewerName: + description: First and last name of the KYC document reviewer. + type: + - string + - 'null' + readOnly: true + reviewStartTime: + description: Date and time when the manual review starts. + type: + - string + - 'null' + format: date-time + readOnly: true + reviewTime: + description: Date and time of manual review. + type: + - string + - 'null' + format: date-time + readOnly: true + notes: + description: Reviewer notes. + type: + - string + - 'null' + tags: + description: List of KYC document tags. + readOnly: true + type: array + items: + $ref: '#/components/schemas/Tag' + reason: + description: Reason for uploading. + type: + - string + - 'null' + matchLevel: + description: Tolerance level setting for document matches. + type: integer + minimum: 1 + maximum: 2 + example: 2 + revision: + description: >- + Number of times the KYC document data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + documentMatches: + type: + - object + - 'null' + readOnly: true + properties: + score: + description: >- + Calculated score that represents the percentage of confidence + that this proof of address represents the customer. + type: number + format: double + example: 0.75 + data: + $ref: '#/components/schemas/KycAddressMatches' + parsedData: + type: + - object + - 'null' + readOnly: true + properties: + score: + description: >- + Calculated score that represents the percentage of confidence + that this proof of address represents the customer. + type: number + format: double + example: 0.75 + data: + $ref: '#/components/schemas/KycAddressMatches' + settings: + description: Settings used to score the document. + readOnly: true + oneOf: + - $ref: '#/components/schemas/KycSettingsAddress' + - type: 'null' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + files: + type: array + maxItems: 3 + description: Linked files. + FundsMatches: + type: object + properties: + firstName: + description: |- + First name of the customer. + This value is null if no match is found. + type: + - string + - 'null' + example: John + lastName: + description: |- + Last name of the customer. + This value is null if no match is found. + type: + - string + - 'null' + example: Doe + documentSubtype: + description: Interpreted subtype of the uploaded document. + type: + - string + - 'null' + $ref: '#/components/schemas/KycDocumentSubtypes' + ProofOfFundsKycDocument: + title: KYC documents. + type: object + required: + - customerId + - documentType + - status + - fileIds + properties: + id: + type: string + readOnly: true + description: ID of the KYC document. + maxLength: 50 + example: kyc_doc_0YV7JHY705C6DA487BFTAA33V8 + fileIds: + description: >- + IDs of linked file objects. + + + Uploaded `identity-proof` files must have the following tags + attached to be used for KYC purposes: + + `['kyc', 'id-front']`, `['kyc', 'id-back']`, `['kyc', + 'face-proof']`. + type: array + items: + type: string + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + documentType: + description: >- + Document type submitted for validation. + + Only the `identity-proof` and `address-proof` types are analyzed + automatically. + $ref: '#/components/schemas/KycDocumentTypes' + documentSubtype: + description: Document subtype submitted for validation. + $ref: '#/components/schemas/KycDocumentSubtypes' + status: + description: Status of the validation. + type: string + readOnly: true + enum: + - pending + - in-progress + - accepted + - rejected + - archived + x-enumDescriptions: + pending: Waiting to be reviewed or analyzed. + in-progress: Being analyzed by the Rebilly AI. + accepted: Accepted by AI or a human. + rejected: Rejected by AI or a human. + archived: Archived by the Rebilly AI. + rejectionReason: + $ref: '#/components/schemas/KycDocumentRejection' + requestId: + readOnly: true + type: + - string + - 'null' + description: ID of the KYC request. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + processedTime: + description: Date and time when the KYC document is processed. + type: + - string + - 'null' + format: date-time + readOnly: true + customerId: + $ref: '#/components/schemas/CustomerId' + reviewerId: + description: ID of the KYC document reviewer. + type: + - string + - 'null' + readOnly: true + maxLength: 50 + example: 44433322-2c4y-483z-a0a9-158621f77a21 + reviewerName: + description: First and last name of the KYC document reviewer. + type: + - string + - 'null' + readOnly: true + reviewStartTime: + description: Date and time when the manual review starts. + type: + - string + - 'null' + format: date-time + readOnly: true + reviewTime: + description: Date and time of manual review. + type: + - string + - 'null' + format: date-time + readOnly: true + notes: + description: Reviewer notes. + type: + - string + - 'null' + tags: + description: List of KYC document tags. + readOnly: true + type: array + items: + $ref: '#/components/schemas/Tag' + reason: + description: Reason for uploading. + type: + - string + - 'null' + matchLevel: + description: Tolerance level setting for document matches. + type: integer + minimum: 1 + maximum: 2 + example: 2 + settings: + description: Settings used to score the document. + type: + - object + - 'null' + revision: + description: >- + Number of times the KYC document data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + documentMatches: + type: + - object + - 'null' + readOnly: true + description: Proof of funds document matches. + properties: + data: + $ref: '#/components/schemas/FundsMatches' + parsedData: + type: + - object + - 'null' + readOnly: true + properties: + data: + $ref: '#/components/schemas/FundsMatches' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + files: + type: array + maxItems: 3 + description: Linked files. + PurchaseMatches: + type: object + properties: + firstName: + description: |- + First name of the customer if it is matched. + This value is null if no match is found. + type: + - string + - 'null' + example: John + lastName: + description: |- + Last name of the customer if it is matched. + This value is null if no match is found. + type: + - string + - 'null' + example: Doe + paymentInstrumentId: + type: + - string + - 'null' + description: |- + ID of the payment instrument related to the document. + This value is null if no match is found. + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + documentSubtype: + description: Interpreted subtype of the uploaded document. + type: + - string + - 'null' + $ref: '#/components/schemas/KycDocumentSubtypes' + ProofOfPurchaseKycDocument: + title: KYC documents. + type: object + required: + - customerId + - documentType + - status + - fileIds + properties: + id: + type: string + readOnly: true + description: ID of the KYC document. + maxLength: 50 + example: kyc_doc_0YV7JHY705C6DA487BFTAA33V8 + fileIds: + description: >- + IDs of linked file objects. + + + Uploaded `identity-proof` files must have the following tags + attached to be used for KYC purposes: + + `['kyc', 'id-front']`, `['kyc', 'id-back']`, `['kyc', + 'face-proof']`. + type: array + items: + type: string + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + documentType: + description: >- + Document type submitted for validation. + + Only the `identity-proof` and `address-proof` types are analyzed + automatically. + $ref: '#/components/schemas/KycDocumentTypes' + documentSubtype: + description: Document subtype submitted for validation. + $ref: '#/components/schemas/KycDocumentSubtypes' + status: + description: Status of the validation. + type: string + readOnly: true + enum: + - pending + - in-progress + - accepted + - rejected + - archived + x-enumDescriptions: + pending: Waiting to be reviewed or analyzed. + in-progress: Being analyzed by the Rebilly AI. + accepted: Accepted by AI or a human. + rejected: Rejected by AI or a human. + archived: Archived by the Rebilly AI. + rejectionReason: + $ref: '#/components/schemas/KycDocumentRejection' + requestId: + readOnly: true + type: + - string + - 'null' + description: ID of the KYC request. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + processedTime: + description: Date and time when the KYC document is processed. + type: + - string + - 'null' + format: date-time + readOnly: true + customerId: + $ref: '#/components/schemas/CustomerId' + reviewerId: + description: ID of the KYC document reviewer. + type: + - string + - 'null' + readOnly: true + maxLength: 50 + example: 44433322-2c4y-483z-a0a9-158621f77a21 + reviewerName: + description: First and last name of the KYC document reviewer. + type: + - string + - 'null' + readOnly: true + reviewStartTime: + description: Date and time when the manual review starts. + type: + - string + - 'null' + format: date-time + readOnly: true + reviewTime: + description: Date and time of manual review. + type: + - string + - 'null' + format: date-time + readOnly: true + notes: + description: Reviewer notes. + type: + - string + - 'null' + tags: + description: List of KYC document tags. + readOnly: true + type: array + items: + $ref: '#/components/schemas/Tag' + reason: + description: Reason for uploading. + type: + - string + - 'null' + matchLevel: + description: Tolerance level setting for document matches. + type: integer + minimum: 1 + maximum: 2 + example: 2 + settings: + description: Settings used to score the document. + type: + - object + - 'null' + revision: + description: >- + Number of times the KYC document data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + documentMatches: + type: + - object + - 'null' + readOnly: true + description: Proof of purchase document matches. + properties: + data: + $ref: '#/components/schemas/PurchaseMatches' + parsedData: + type: + - object + - 'null' + readOnly: true + description: Parsed data. + properties: + data: + $ref: '#/components/schemas/PurchaseMatches' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + - paymentInstrument + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + files: + type: array + maxItems: 3 + description: Linked files. + CreditFileCommonDecisionData: + readOnly: true + type: object + properties: + lastNameMatch: + description: Last name match. + type: string + readOnly: true + example: 'Y' + firstNameMatch: + description: First name match. + type: string + readOnly: true + example: 'Y' + civicNumberMatch: + description: Civic number match. + type: string + readOnly: true + example: 'Y' + streetNameMatch: + description: Street name match. + type: string + readOnly: true + example: 'Y' + cityMatch: + description: City match. + type: string + readOnly: true + example: 'Y' + postalCodeMatch: + description: Postal code match. + type: string + readOnly: true + example: 'Y' + provinceMatch: + description: Province match. + type: string + readOnly: true + example: 'Y' + dateOfBirthMatch: + description: Date of birth match. + type: string + readOnly: true + example: 'Y' + ageOfCreditFileThreeOrMoreYearsOld: + description: Age of credit file is three or more years old. + type: string + readOnly: true + example: 'Y' + addressAsReported: + description: Address as reported. + type: string + readOnly: true + example: 'Y' + nameAsReported: + description: Name as reported. + type: string + readOnly: true + example: 'Y' + dateOfBirthAsReported: + description: Date of birth as reported. + type: string + readOnly: true + example: 'Y' + CreditFileMatches: + type: object + properties: + creditBureau: + description: Credit bureau from which the credit file data is sourced. + type: string + readOnly: true + enum: + - equifax + - experian + - transunion + - test-bureau + creditFileNumber: + description: Credit file number from the credit bureau. + type: string + readOnly: true + name: + description: Name of the person to consult about the credit file. + type: string + readOnly: true + consultedDate: + description: Date to consult with the credit bureau about the credit file. + type: string + format: date-time + readOnly: true + decision: + description: >- + Specifies if the credit file is used in a single source, or as part + of a dual source, proof of identity decision. + type: string + enum: + - single-source + - dual-source + - other + x-enumDescriptions: + single-source: >- + Credit file used as a single source of proof for an identity + decisions. + dual-source: >- + Credit file used as a source of proof where the credit bureau + agency found two reliable trade sources. + + At least two sources are required for a dual-source proof of + identity decision. + other: Credit file cannot be used for KYC decisions. + readOnly: true + trades: + description: Contains information to support the dual process. + type: array + items: + type: object + properties: + name: + type: string + description: Trade name. + example: Rogers + accountNumber: + type: string + example: 123-ABC-123 + dateOpened: + type: string + format: date + readOnly: true + referenceData: + description: Extra data from the credit bureau. + readOnly: true + type: object + properties: + singleSourceHit: + description: Single source hit. + type: string + readOnly: true + example: 'Y' + dualSourceHit: + description: Dual source hit. + type: string + readOnly: true + example: 'Y' + waterfallProcess: + description: Waterfall process. + type: string + readOnly: true + example: 'Y' + creditFileCreatedDate: + description: Date when the credit file is created. + type: string + readOnly: true + numberOfTradesOnFile: + description: Number of trades on file. + type: string + readOnly: true + example: '001' + singleDecision: + description: Data related to a single source decision. + type: object + allOf: + - $ref: '#/components/schemas/CreditFileCommonDecisionData' + dualDecision: + description: Data related to a dual source decision. + type: + - array + - 'null' + items: + allOf: + - $ref: '#/components/schemas/CreditFileCommonDecisionData' + ProofOfCreditFileKycDocument: + title: KYC documents. + type: object + required: + - customerId + - documentType + - status + - fileIds + properties: + id: + type: string + readOnly: true + description: ID of the KYC document. + maxLength: 50 + example: kyc_doc_0YV7JHY705C6DA487BFTAA33V8 + fileIds: + description: >- + IDs of linked file objects. + + + Uploaded `identity-proof` files must have the following tags + attached to be used for KYC purposes: + + `['kyc', 'id-front']`, `['kyc', 'id-back']`, `['kyc', + 'face-proof']`. + type: array + items: + type: string + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + documentType: + description: >- + Document type submitted for validation. + + Only the `identity-proof` and `address-proof` types are analyzed + automatically. + $ref: '#/components/schemas/KycDocumentTypes' + documentSubtype: + description: Document subtype submitted for validation. + $ref: '#/components/schemas/KycDocumentSubtypes' + status: + description: Status of the validation. + type: string + readOnly: true + enum: + - pending + - in-progress + - accepted + - rejected + - archived + x-enumDescriptions: + pending: Waiting to be reviewed or analyzed. + in-progress: Being analyzed by the Rebilly AI. + accepted: Accepted by AI or a human. + rejected: Rejected by AI or a human. + archived: Archived by the Rebilly AI. + rejectionReason: + $ref: '#/components/schemas/KycDocumentRejection' + requestId: + readOnly: true + type: + - string + - 'null' + description: ID of the KYC request. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + processedTime: + description: Date and time when the KYC document is processed. + type: + - string + - 'null' + format: date-time + readOnly: true + customerId: + $ref: '#/components/schemas/CustomerId' + reviewerId: + description: ID of the KYC document reviewer. + type: + - string + - 'null' + readOnly: true + maxLength: 50 + example: 44433322-2c4y-483z-a0a9-158621f77a21 + reviewerName: + description: First and last name of the KYC document reviewer. + type: + - string + - 'null' + readOnly: true + reviewStartTime: + description: Date and time when the manual review starts. + type: + - string + - 'null' + format: date-time + readOnly: true + reviewTime: + description: Date and time of manual review. + type: + - string + - 'null' + format: date-time + readOnly: true + notes: + description: Reviewer notes. + type: + - string + - 'null' + tags: + description: List of KYC document tags. + readOnly: true + type: array + items: + $ref: '#/components/schemas/Tag' + reason: + description: Reason for uploading. + type: + - string + - 'null' + matchLevel: + description: Tolerance level setting for document matches. + type: integer + minimum: 1 + maximum: 2 + example: 2 + settings: + description: Settings used to score the document. + type: + - object + - 'null' + revision: + description: >- + Number of times the KYC document data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + documentMatches: + description: Proof of credit document matches. + type: + - object + - 'null' + readOnly: true + properties: + data: + $ref: '#/components/schemas/CreditFileMatches' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + files: + type: array + maxItems: 3 + description: Linked files. + KycDocument: + type: object + description: KYC document information. + discriminator: + propertyName: documentType + mapping: + identity-proof: '#/components/schemas/ProofOfIdentityKycDocument' + address-proof: '#/components/schemas/ProofOfAddressKycDocument' + funds-proof: '#/components/schemas/ProofOfFundsKycDocument' + purchase-proof: '#/components/schemas/ProofOfPurchaseKycDocument' + credit-file-proof: '#/components/schemas/ProofOfCreditFileKycDocument' + oneOf: + - $ref: '#/components/schemas/ProofOfIdentityKycDocument' + - $ref: '#/components/schemas/ProofOfAddressKycDocument' + - $ref: '#/components/schemas/ProofOfFundsKycDocument' + - $ref: '#/components/schemas/ProofOfPurchaseKycDocument' + - $ref: '#/components/schemas/ProofOfCreditFileKycDocument' + KycSettings: + type: object + description: Settings for KYC score calculation. + properties: + identityProof: + $ref: '#/components/schemas/KycSettingsIdentity' + addressProof: + $ref: '#/components/schemas/KycSettingsAddress' + ResetPasswordToken: + type: object + required: + - username + properties: + token: + description: ID of the token. + type: string + readOnly: true + username: + description: Username associated with the token. + type: string + credentialId: + description: Credential ID associated with the token. + type: string + readOnly: true + expiredTime: + description: Date and time when the password expires. + type: + - string + - 'null' + format: date-time + _links: + $ref: '#/components/schemas/SelfLink' + PaymentCardBrand: + description: Brand of payment card. + type: string + enum: + - Visa + - MasterCard + - American Express + - Discover + - Maestro + - Solo + - Electron + - JCB + - Voyager + - Diners Club + - Switch + - Laser + - China UnionPay + - AstroPay Card + UseAsBackup: + description: >- + Specifies if this payment instrument can be used as a backup for invoice + payment retries. + type: boolean + default: false + PaymentCard: + type: object + description: Payment card information. + title: Payment card + required: + - method + properties: + id: + type: string + description: ID of the payment instrument. + readOnly: true + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + customerId: + $ref: '#/components/schemas/CustomerId' + method: + description: Method of payment instrument. + type: string + readOnly: true + enum: + - payment-card + status: + type: string + description: >- + Status of the payment instrument. + + An `active` status means that a payment instrument has been used at + least once for an approved transaction. + + To remove an instrument from use, set this value to `deactivated`. + + + For more information, see [Deactivate a payment + instrument](./PostPaymentInstrumentDeactivation). + enum: + - active + - inactive + - expired + - deactivated + - verification-needed + fingerprint: + description: |- + Unique value that is used to identify the payment instrument. + This value is generated from the `bin` and the `last4` values. + This value contains alphanumeric characters. + type: string + readOnly: true + bin: + description: >- + Bank Identification Number (BIN) of the payment card. + + This value is the same as the first 6 digits of the associated + Primary Account Number (PAN). + type: string + format: bin + readOnly: true + last4: + description: Last 4 digits of the associated Primary Account Number (PAN). + type: string + readOnly: true + pan: + description: Primary Account Number (PAN) of the payment card. + type: string + writeOnly: true + expYear: + description: Expiration year of the payment card. + type: integer + expMonth: + description: Expiration month of the payment card. + type: integer + cvv: + description: Card Verification Value (CVV) of the payment card. + type: string + writeOnly: true + brand: + readOnly: true + allOf: + - $ref: '#/components/schemas/PaymentCardBrand' + bankCountry: + description: Bank country of the payment instrument. + type: + - string + - 'null' + readOnly: true + bankName: + description: Bank name of the payment instrument. + type: string + readOnly: true + billingAddress: + description: Contact's billing address. + $ref: '#/components/schemas/ContactObject' + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + billingPortalUrl: + description: URL of the billing portal where the card can be updated. + type: string + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + stickyGatewayAccountId: + description: >- + ID of the sticky gateway account. + + All future payments are processed by this gateway account. + + + For more information, + + see [Gateway + routing](https://www.rebilly.com/docs/settings/intelligent-payment-routing/#sticky-gateway-accounts). + type: + - string + - 'null' + readOnly: true + expirationReminderTime: + description: Date and time when an expiration reminder event is triggered. + type: + - string + - 'null' + format: date-time + readOnly: true + expirationReminderNumber: + description: Number of expiration reminder events that have triggered. + type: + - integer + - 'null' + readOnly: true + referenceData: + description: Payment instrument reference data. + type: object + additionalProperties: + type: string + example: + gatewayTransactionId: GAT123 + digitalWallet: + readOnly: true + description: Digital wallet type. + type: + - string + - 'null' + enum: + - Apple Pay + - Google Pay + - null + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + revision: + description: >- + Number of times the payment instrument data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + - authTransaction + - approvalUrl + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + authTransaction: + type: object + customer: + type: object + BankAccount: + type: object + title: Bank account + required: + - method + properties: + id: + type: string + description: ID of the payment instrument. + readOnly: true + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + customerId: + $ref: '#/components/schemas/CustomerId' + method: + description: Method of payment instrument. + type: string + readOnly: true + enum: + - ach + bankName: + description: Name of the bank. + type: string + routingNumber: + description: Bank routing number. + type: string + accountNumberType: + description: >- + Type of bank account number. + + A valid value is a Basic Bank Account Number (BBAN) or an + International Bank Account Number (IBAN). + type: string + default: BBAN + enum: + - BBAN + - IBAN + accountType: + description: Type of bank account. + type: string + enum: + - checking + - savings + - other + bic: + description: Bank Identifier Code (BIC). + type: + - string + - 'null' + billingAddress: + description: Customer's billing address. + $ref: '#/components/schemas/ContactObject' + fingerprint: + description: |- + Unique value which identifies the bank account. + This value contains alphanumeric characters. + Depending on the type of bank account number, + a bank account fingerprint is generated using one of the following: + - BBAN: `last4` and `routingNumber` values. + - IBAN: First 8 characters of the IBAN and the `last4` value. + type: string + readOnly: true + last4: + description: Last 4 digits of the bank account number. + type: string + readOnly: true + status: + description: Status of the bank account. + type: string + readOnly: true + enum: + - active + - deactivated + stickyGatewayAccountId: + description: >- + ID of the sticky gateway account. + + All future payments are processed by this gateway account. + + + For more information, see [Gateway + routing](https://www.rebilly.com/docs/settings/intelligent-payment-routing/#sticky-gateway-accounts). + type: + - string + - 'null' + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + revision: + description: >- + Number of times the payment instrument data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + PayPalAccount: + type: object + title: PayPal account + required: + - method + - customerId + - billingAddress + properties: + id: + type: string + description: ID of the payment instrument. + readOnly: true + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + customerId: + $ref: '#/components/schemas/CustomerId' + method: + description: Method of payment instrument. + type: string + enum: + - paypal + billingAddress: + description: Billing address. + $ref: '#/components/schemas/ContactObject' + username: + description: PayPal username. + type: string + readOnly: true + status: + description: PayPal account status. + type: string + readOnly: true + enum: + - inactive + - active + - deactivated + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + stickyGatewayAccountId: + description: >- + ID of the sticky gateway account. + + All future payments are processed by this gateway account. + + + For more information, see [Gateway + routing](https://www.rebilly.com/docs/settings/intelligent-payment-routing/#sticky-gateway-accounts). + type: + - string + - 'null' + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + revision: + description: >- + Number of times the payment instrument data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + - authTransaction + - approvalUrl + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + authTransaction: + type: object + customer: + type: object + KhelocardCard: + type: object + title: Khelocard card + required: + - method + properties: + id: + type: string + description: ID of the payment instrument. + readOnly: true + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + customerId: + $ref: '#/components/schemas/CustomerId' + method: + description: Method of payment instrument. + type: string + enum: + - Khelocard + fingerprint: + description: >- + Unique value which identifies the payment instrument. + + This value contains alphanumeric characters. + + This value is generated from the card number, CVV, and expiration + date. + type: string + number: + description: Khelocard card masked number. + type: string + last4: + description: Last 4 digits of the number. + type: string + expYear: + description: Khelocard card expiration year. + type: integer + expMonth: + description: Khelocard card expiration month. + type: integer + billingAddress: + description: Billing address. + $ref: '#/components/schemas/ContactObject' + status: + description: Payment instrument status. + type: string + enum: + - active + - deactivated + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + stickyGatewayAccountId: + description: >- + ID of the sticky gateway account. + + All future payments are processed by this gateway account. + + + For more information, + + see [Gateway + routing](https://www.rebilly.com/docs/settings/intelligent-payment-routing/#sticky-gateway-accounts). + type: + - string + - 'null' + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + revision: + description: >- + Number of times the payment instrument data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + AlternativePaymentMethods: + type: string + description: Payment method. + enum: + - cash + - check + - paypal + - AdvCash + - Aircash + - Alfa-click + - Alipay + - AstroPay Card + - AstroPay-GO + - BankReferenced + - bank-transfer + - bank-transfer-2 + - bank-transfer-3 + - bank-transfer-4 + - bank-transfer-5 + - bank-transfer-6 + - bank-transfer-7 + - bank-transfer-8 + - bank-transfer-9 + - Baloto + - Beeline + - Belfius-direct-net + - bitcoin + - Bizum + - Boleto + - cash-deposit + - CASHlib + - CashToCode + - China UnionPay + - Cleo + - CODVoucher + - Conekta-oxxo + - Cupon-de-pagos + - cryptocurrency + - domestic-cards + - Directa24Card + - echeck + - ecoPayz + - ecoVoucher + - Efecty + - EPS + - ePay.bg + - eZeeWallet + - FasterPay + - Flexepin + - Giropay + - Gpaysafe + - Google Pay + - iDebit + - iDEAL + - ING-homepay + - INOVAPAY-pin + - INOVAPAY-wallet + - InstaDebit + - instant-bank-transfer + - InstantPayments + - Interac + - Interac-online + - Interac-eTransfer + - invoice + - iWallet + - Jeton + - jpay + - KakaoPay + - Khelocard + - Klarna + - KNOT + - loonie + - Matrix + - MaxiCash + - Megafon + - MercadoPago + - MiFinity-eWallet + - miscellaneous + - MobilePay + - Bancontact + - Bancontact-mobile + - MTS + - MuchBetter + - Multibanco + - Neosurf + - Netbanking + - Neteller + - Nordea-Solo + - OchaPay + - online-bank-transfer + - Onlineueberweisen + - oriental-wallet + - OXXO + - P24 + - Pagadito + - PagoEffectivo + - Pagsmile-deposit-express + - Pagsmile-lottery + - PayCash + - Payco + - Payeer + - PaymentAsia-crypto + - Paymero + - Perfect-money + - Piastrix + - PIX + - plaid-account + - PayTabs + - Paysafecard + - Paysafecash + - Pay4Fun + - Paynote + - PinPay + - phone + - PhonePe + - POLi + - PostFinance-card + - PostFinance-e-finance + - QIWI + - QPay + - QQPay + - rapyd-checkout + - Resurs + - SafetyPay + - Samsung Pay + - SEPA + - Skrill + - Skrill Rapid Transfer + - SMSVoucher + - Sofort + - SparkPay + - swift-dbt + - Tele2 + - Terminaly-RF + - ToditoCash-card + - Trustly + - Tupay + - UPayCard + - UPI + - USD-coin + - VCreditos + - VenusPoint + - voucher + - voucher-2 + - voucher-3 + - voucher-4 + - Wallet88 + - Webmoney + - Webpay + - Webpay-2 + - Webpay Card + - WeChat Pay + - wire + - XPay-P2P + - XPay-QR + - Yandex-money + - Zotapay + - Zimpler + AlternativeInstrument: + type: object + title: Alternative instrument + required: + - method + - customerId + - billingAddress + properties: + id: + type: string + description: ID of the payment instrument. + readOnly: true + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + customerId: + $ref: '#/components/schemas/CustomerId' + method: + description: Payment method of the payment instrument. + allOf: + - $ref: '#/components/schemas/AlternativePaymentMethods' + - not: + enum: + - payment-card + - paypal + - ach + - echeck + - Khelocard + billingAddress: + description: >- + Billing address of the user that is associated with the payment + instrument. + $ref: '#/components/schemas/ContactObject' + status: + description: Payment instrument status. + type: string + readOnly: true + enum: + - active + - deactivated + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + stickyGatewayAccountId: + description: >- + ID of the sticky gateway account. + + All future payments are processed by this gateway account. + + + For more information, + + see [Gateway + routing](https://www.rebilly.com/docs/settings/intelligent-payment-routing/#sticky-gateway-accounts). + type: + - string + - 'null' + readOnly: true + referenceData: + description: Payment instrument reference data. + type: object + additionalProperties: + type: string + example: + gatewayTransactionId: GAT123 + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + revision: + description: >- + Number of times the payment instrument data has been modified. + + + Use this value when analyzing webhook data to determine if a change + must take precedence over the current representation. + type: integer + readOnly: true + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - customer + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + PaymentInstrument: + discriminator: + propertyName: method + mapping: + payment-card: '#/components/schemas/PaymentCard' + ach: '#/components/schemas/BankAccount' + paypal: '#/components/schemas/PayPalAccount' + Khelocard: '#/components/schemas/KhelocardCard' + cash: '#/components/schemas/AlternativeInstrument' + check: '#/components/schemas/AlternativeInstrument' + AdvCash: '#/components/schemas/AlternativeInstrument' + Alfa-click: '#/components/schemas/AlternativeInstrument' + Alipay: '#/components/schemas/AlternativeInstrument' + AstroPay Card: '#/components/schemas/AlternativeInstrument' + AstroPay-GO: '#/components/schemas/AlternativeInstrument' + BankReferenced: '#/components/schemas/AlternativeInstrument' + bank-transfer: '#/components/schemas/AlternativeInstrument' + bank-transfer-2: '#/components/schemas/AlternativeInstrument' + bank-transfer-3: '#/components/schemas/AlternativeInstrument' + bank-transfer-4: '#/components/schemas/AlternativeInstrument' + bank-transfer-5: '#/components/schemas/AlternativeInstrument' + bank-transfer-6: '#/components/schemas/AlternativeInstrument' + bank-transfer-7: '#/components/schemas/AlternativeInstrument' + bank-transfer-8: '#/components/schemas/AlternativeInstrument' + bank-transfer-9: '#/components/schemas/AlternativeInstrument' + Baloto: '#/components/schemas/AlternativeInstrument' + Beeline: '#/components/schemas/AlternativeInstrument' + Belfius-direct-net: '#/components/schemas/AlternativeInstrument' + bitcoin: '#/components/schemas/AlternativeInstrument' + Bizum: '#/components/schemas/AlternativeInstrument' + Boleto: '#/components/schemas/AlternativeInstrument' + cash-deposit: '#/components/schemas/AlternativeInstrument' + CASHlib: '#/components/schemas/AlternativeInstrument' + CashToCode: '#/components/schemas/AlternativeInstrument' + China UnionPay: '#/components/schemas/AlternativeInstrument' + Cleo: '#/components/schemas/AlternativeInstrument' + CODVoucher: '#/components/schemas/AlternativeInstrument' + Conekta-oxxo: '#/components/schemas/AlternativeInstrument' + Cupon-de-pagos: '#/components/schemas/AlternativeInstrument' + cryptocurrency: '#/components/schemas/AlternativeInstrument' + domestic-cards: '#/components/schemas/AlternativeInstrument' + ecoPayz: '#/components/schemas/AlternativeInstrument' + ecoVoucher: '#/components/schemas/AlternativeInstrument' + Efecty: '#/components/schemas/AlternativeInstrument' + EPS: '#/components/schemas/AlternativeInstrument' + ePay.bg: '#/components/schemas/AlternativeInstrument' + eZeeWallet: '#/components/schemas/AlternativeInstrument' + FasterPay: '#/components/schemas/AlternativeInstrument' + Flexepin: '#/components/schemas/AlternativeInstrument' + Giropay: '#/components/schemas/AlternativeInstrument' + Gpaysafe: '#/components/schemas/AlternativeInstrument' + Google Pay: '#/components/schemas/AlternativeInstrument' + iDebit: '#/components/schemas/AlternativeInstrument' + iDEAL: '#/components/schemas/AlternativeInstrument' + ING-homepay: '#/components/schemas/AlternativeInstrument' + INOVAPAY-pin: '#/components/schemas/AlternativeInstrument' + INOVAPAY-wallet: '#/components/schemas/AlternativeInstrument' + InstaDebit: '#/components/schemas/AlternativeInstrument' + instant-bank-transfer: '#/components/schemas/AlternativeInstrument' + InstantPayments: '#/components/schemas/AlternativeInstrument' + Interac: '#/components/schemas/AlternativeInstrument' + Interac-online: '#/components/schemas/AlternativeInstrument' + Interac-eTransfer: '#/components/schemas/AlternativeInstrument' + invoice: '#/components/schemas/AlternativeInstrument' + iWallet: '#/components/schemas/AlternativeInstrument' + Jeton: '#/components/schemas/AlternativeInstrument' + jpay: '#/components/schemas/AlternativeInstrument' + Klarna: '#/components/schemas/AlternativeInstrument' + KNOT: '#/components/schemas/AlternativeInstrument' + loonie: '#/components/schemas/AlternativeInstrument' + Matrix: '#/components/schemas/AlternativeInstrument' + MaxiCash: '#/components/schemas/AlternativeInstrument' + Megafon: '#/components/schemas/AlternativeInstrument' + MiFinity-eWallet: '#/components/schemas/AlternativeInstrument' + miscellaneous: '#/components/schemas/AlternativeInstrument' + Bancontact: '#/components/schemas/AlternativeInstrument' + Bancontact-mobile: '#/components/schemas/AlternativeInstrument' + MTS: '#/components/schemas/AlternativeInstrument' + MuchBetter: '#/components/schemas/AlternativeInstrument' + Multibanco: '#/components/schemas/AlternativeInstrument' + Neosurf: '#/components/schemas/AlternativeInstrument' + Netbanking: '#/components/schemas/AlternativeInstrument' + Neteller: '#/components/schemas/AlternativeInstrument' + Nordea-Solo: '#/components/schemas/AlternativeInstrument' + OchaPay: '#/components/schemas/AlternativeInstrument' + online-bank-transfer: '#/components/schemas/AlternativeInstrument' + Onlineueberweisen: '#/components/schemas/AlternativeInstrument' + oriental-wallet: '#/components/schemas/AlternativeInstrument' + OXXO: '#/components/schemas/AlternativeInstrument' + P24: '#/components/schemas/AlternativeInstrument' + Pagadito: '#/components/schemas/AlternativeInstrument' + PagoEffectivo: '#/components/schemas/AlternativeInstrument' + Pagsmile-deposit-express: '#/components/schemas/AlternativeInstrument' + Pagsmile-lottery: '#/components/schemas/AlternativeInstrument' + PayCash: '#/components/schemas/AlternativeInstrument' + Payeer: '#/components/schemas/AlternativeInstrument' + PaymentAsia-crypto: '#/components/schemas/AlternativeInstrument' + Paymero: '#/components/schemas/AlternativeInstrument' + Perfect-money: '#/components/schemas/AlternativeInstrument' + Piastrix: '#/components/schemas/AlternativeInstrument' + plaid-account: '#/components/schemas/AlternativeInstrument' + PayTabs: '#/components/schemas/AlternativeInstrument' + Paysafecard: '#/components/schemas/AlternativeInstrument' + Paysafecash: '#/components/schemas/AlternativeInstrument' + Pay4Fun: '#/components/schemas/AlternativeInstrument' + Paynote: '#/components/schemas/AlternativeInstrument' + PinPay: '#/components/schemas/AlternativeInstrument' + phone: '#/components/schemas/AlternativeInstrument' + PhonePe: '#/components/schemas/AlternativeInstrument' + POLi: '#/components/schemas/AlternativeInstrument' + PostFinance-card: '#/components/schemas/AlternativeInstrument' + PostFinance-e-finance: '#/components/schemas/AlternativeInstrument' + QIWI: '#/components/schemas/AlternativeInstrument' + QPay: '#/components/schemas/AlternativeInstrument' + QQPay: '#/components/schemas/AlternativeInstrument' + rapyd-checkout: '#/components/schemas/AlternativeInstrument' + Resurs: '#/components/schemas/AlternativeInstrument' + SafetyPay: '#/components/schemas/AlternativeInstrument' + SEPA: '#/components/schemas/AlternativeInstrument' + Skrill: '#/components/schemas/AlternativeInstrument' + Skrill Rapid Transfer: '#/components/schemas/AlternativeInstrument' + SMSVoucher: '#/components/schemas/AlternativeInstrument' + Sofort: '#/components/schemas/AlternativeInstrument' + SparkPay: '#/components/schemas/AlternativeInstrument' + swift-dbt: '#/components/schemas/AlternativeInstrument' + Tele2: '#/components/schemas/AlternativeInstrument' + Terminaly-RF: '#/components/schemas/AlternativeInstrument' + ToditoCash-card: '#/components/schemas/AlternativeInstrument' + Trustly: '#/components/schemas/AlternativeInstrument' + UPayCard: '#/components/schemas/AlternativeInstrument' + UPI: '#/components/schemas/AlternativeInstrument' + USD-coin: '#/components/schemas/AlternativeInstrument' + VCreditos: '#/components/schemas/AlternativeInstrument' + VenusPoint: '#/components/schemas/AlternativeInstrument' + voucher: '#/components/schemas/AlternativeInstrument' + voucher-2: '#/components/schemas/AlternativeInstrument' + voucher-3: '#/components/schemas/AlternativeInstrument' + voucher-4: '#/components/schemas/AlternativeInstrument' + Webmoney: '#/components/schemas/AlternativeInstrument' + Webpay: '#/components/schemas/AlternativeInstrument' + Webpay-2: '#/components/schemas/AlternativeInstrument' + Webpay Card: '#/components/schemas/AlternativeInstrument' + WeChat Pay: '#/components/schemas/AlternativeInstrument' + XPay-P2P: '#/components/schemas/AlternativeInstrument' + XPay-QR: '#/components/schemas/AlternativeInstrument' + Yandex-money: '#/components/schemas/AlternativeInstrument' + Zotapay: '#/components/schemas/AlternativeInstrument' + Zimpler: '#/components/schemas/AlternativeInstrument' + anyOf: + - $ref: '#/components/schemas/PaymentCard' + - $ref: '#/components/schemas/BankAccount' + - $ref: '#/components/schemas/PayPalAccount' + - $ref: '#/components/schemas/KhelocardCard' + - $ref: '#/components/schemas/AlternativeInstrument' + PaymentInstrumentCreateToken: + title: Payment token + type: object + required: + - customerId + - token + properties: + customerId: + $ref: '#/components/schemas/CustomerId' + token: + description: ID of the payment token. + type: string + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + PaymentCardCreatePlain: + title: Payment card + type: object + required: + - method + - customerId + - pan + - expYear + - expMonth + - billingAddress + properties: + method: + description: Method of the payment instrument. + type: string + enum: + - payment-card + customerId: + $ref: '#/components/schemas/CustomerId' + pan: + description: Primary Account Number (PAN) of the payment card. + type: string + writeOnly: true + expYear: + description: Expiration year of the payment card. + type: integer + expMonth: + description: Expiration month of the payment card. + type: integer + cvv: + description: Card Verification Value (CVV) of the payment card. + type: string + writeOnly: true + billingAddress: + description: Billing address. + $ref: '#/components/schemas/ContactObject' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + BankAccountCreatePlain: + title: Bank account + type: object + required: + - accountNumberType + discriminator: + propertyName: accountNumberType + mapping: + BBAN: '#/components/schemas/BBANType' + IBAN: '#/components/schemas/IBANType' + properties: + accountNumberType: + type: string + enum: + - IBAN + - BBAN + BBANType: + type: object + description: BBAN type object. + required: + - accountNumberType + - method + - customerId + - accountNumber + - routingNumber + - accountType + - billingAddress + allOf: + - $ref: '#/components/schemas/BankAccountCreatePlain' + - properties: + method: + description: Payment method of the payment instrument. + type: string + enum: + - ach + customerId: + $ref: '#/components/schemas/CustomerId' + accountNumberType: + description: >- + Bank account number type. + + A valid value is a Basic Bank Account Number (BBAN) or an + International Bank Account Number (IBAN). + type: string + default: BBAN + enum: + - IBAN + - BBAN + accountNumber: + description: >- + Customer's bank account number. + + Detailed information on all ISO 13616-compliant national IBAN + formats is available in the [SWIFT IBAN + Registry](https://www.swift.com/standards/data-standards/iban). + type: string + pattern: '^[0-9]+$' + routingNumber: + description: Bank routing number. + type: string + pattern: '^[0-9]+$' + accountType: + description: Bank account type. + type: string + enum: + - checking + - savings + - other + bankName: + description: Bank name. + type: string + bic: + description: Bank Identifier Code (BIC). + type: string + billingAddress: + description: Customer's billing address. + $ref: '#/components/schemas/ContactObject' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + IBANType: + type: object + required: + - accountNumberType + - method + - customerId + - accountNumber + - billingAddress + description: IBAN type object. + allOf: + - $ref: '#/components/schemas/BankAccountCreatePlain' + - properties: + method: + description: Payment method of the payment instrument. + type: string + enum: + - ach + customerId: + $ref: '#/components/schemas/CustomerId' + accountNumberType: + description: >- + Customer's bank account number type. + + A valid value is a Basic Bank Account Number (BBAN) or an + International Bank Account Number (IBAN). + type: string + default: BBAN + enum: + - IBAN + - BBAN + accountNumber: + description: >- + Customer's bank account number. + + Detailed information on all ISO 13616-compliant national IBAN + formats is available in the [SWIFT IBAN + Registry](https://www.swift.com/standards/data-standards/iban). + type: string + bankName: + description: Bank name. + type: string + bic: + description: Bank Identifier Code (BIC). + type: string + billingAddress: + description: Customer's billing address. + $ref: '#/components/schemas/ContactObject' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + PaymentInstrumentUpdateToken: + type: object + properties: + token: + description: ID of the payment token. + type: string + billingAddress: + description: >- + Customer's billing address. + + If this value is supplied it overrides the billing address that is + supplied with the token. + $ref: '#/components/schemas/ContactObject' + stickyGatewayAccountId: + description: >- + ID of the sticky gateway account. + + All future payments are processed by this gateway account. + + + For more information, + + see [Gateway + routing](https://www.rebilly.com/docs/settings/intelligent-payment-routing/#sticky-gateway-accounts). + $ref: '#/components/schemas/ResourceId' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + PaymentCardUpdatePlain: + type: object + properties: + cvv: + description: Card Verification Value (CVV) of the payment card. + type: string + expMonth: + description: Expiration month of the payment card. + type: integer + expYear: + description: Expiration year of the payment card. + type: integer + billingAddress: + description: Billing address. + $ref: '#/components/schemas/ContactObject' + stickyGatewayAccountId: + description: >- + ID of the sticky gateway account. + + All future payments are processed by this gateway account. + + + For more information, + + see [Gateway + routing](https://www.rebilly.com/docs/settings/intelligent-payment-routing/#sticky-gateway-accounts). + $ref: '#/components/schemas/ResourceId' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + BankAccountUpdatePlain: + type: object + properties: + bankName: + description: Name of the bank. + type: string + accountType: + description: Bank account type. + type: string + enum: + - checking + - savings + - other + billingAddress: + description: Billing address. + $ref: '#/components/schemas/ContactObject' + stickyGatewayAccountId: + description: >- + ID of the sticky gateway account. + + All future payments are processed by this gateway account. + + + For more information, + + see [Gateway + routing](https://www.rebilly.com/docs/settings/intelligent-payment-routing/#sticky-gateway-accounts). + $ref: '#/components/schemas/ResourceId' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + PlanFormulaFixedFee: + title: Fixed-fee + description: >- + Fixed-fee pricing details. + + Use this formula for subscriptions that involve the same price, + + number of units, and reoccur over a fixed period of time. + + + For more information, see [Fixed-fee per + period](https://www.rebilly.com/docs/settings/pricing-formulas/#fixed-fee-per-period). + type: object + required: + - price + - formula + properties: + formula: + type: string + description: >- + Price formula that determines which algorithm is used to calculate + the invoice price based on the following factors: + + + - Quantity in the order. + This value may be variable if you are charging based on usage. + If you are not charging based on usage, + this value is determined when an order is created. + + - Price of the quantity range. + Some formulas set a price based on defined product quantity ranges. + + For more information, see [Price + formulas](https://www.rebilly.com/docs/billing/pricing-formulas/). + enum: + - fixed-fee + price: + description: >- + Use this formula to charge for subscriptions that have a fixed + price, + + number of units, and reoccur over a fixed period of time, + + such as every: day, week, month, year, or number of years. + + + For example, a streaming company charges customers $13.99 each month + for subscription period that is 6 months in duration. + + The quantity of movies that a customer streams each month does not + impact the price. + type: number + format: double + example: 99.95 + PlanFormulaFlatRate: + title: Flat rate + description: >- + Flat rate pricing details. + + Use this formula to charge a flat fee per unit. + + For example, $0.10 per transaction or $4 per unit. + + + For more information, see [Flat rate + pricing](https://www.rebilly.com/docs/billing/pricing-formulas/#flat-rate-pricing). + type: object + required: + - price + - formula + properties: + formula: + type: string + description: >- + Price formula that determines which algorithm is used to calculate + the invoice price based on the following factors: + + + - Quantity in the order. + This value may be variable if you are charging based on usage. + If you are not charging based on usage, + this value is determined when an order is created. + + - Price of the quantity range. + Some formulas set a price based on defined product quantity ranges. + + For more information, see [Price + formulas](https://www.rebilly.com/docs/billing/pricing-formulas/). + enum: + - flat-rate + price: + description: |- + Use this formula to charge a flat fee per unit. + For example, $0.10 for each transaction or $4 per unit. + + If the value of this field is `0`, the product is free. + type: number + format: double + example: 99.95 + minQuantity: + description: |- + Minimum permitted unit quantity. + If this value is `null`, no limit is in place. + type: + - integer + - 'null' + default: null + example: 1 + minimum: 1 + maximum: 65535 + maxQuantity: + description: |- + Maximum permitted unit quantity. + If this value is `null`, no limit is in place. + type: + - integer + - 'null' + example: 1 + minimum: 1 + maximum: 65535 + PlanFormulaStairstep: + title: Stair-step + description: >- + Stair-step pricing details. + + Use this formula to charge for units that are sold in specific + quantities. + + + For more information, see + [Stair-step](https://www.rebilly.com/docs/billing/pricing-formulas/#stair-step). + type: object + required: + - brackets + - formula + properties: + formula: + type: string + description: >- + Price formula that determines which algorithm is used to calculate + the invoice price based on the following factors: + + + - Quantity in the order. + This value may be variable if you are charging based on usage. + If you are not charging based on usage, + this value is determined when an order is created. + + - Price of the quantity range. + Some formulas set a price based on defined product quantity ranges. + + For more information, see [Price + formulas](https://www.rebilly.com/docs/billing/pricing-formulas/). + enum: + - stairstep + brackets: + description: >- + Use this formula to charge for units that are sold in specific + quantity ranges. + + + For example, a bank charges a merchant based on the number of + transactions they complete each month. + + + Price total | Max quantity | Description + + ------------|--------------|------------ + + $50 | 1000 | Up to 1000 transactions + + $100 | 1500 | Between 1000 and 1500 transactions + + $200 | 3000 | Between 1500 and 3000 transactions + + $500 | null | More than 3000 transactions + + + - If the merchant completes 900 transaction in a month, they are + charged $50. + + - If the merchant completes 1001 transaction in a month, they are + charged $100. + + - If the merchant completes 2500 transaction in a month, they are + charged $200. + + - If the merchant completes more than 3000 transaction in a month, + they are charged $500. + type: array + minItems: 1 + items: + type: object + properties: + price: + description: |- + Price of the quantity range. + If the value of this field is `0`, the product is free. + type: number + format: double + example: 99.95 + maxQuantity: + description: |- + Maximum permitted unit quantity. + If this value is `null`, no limit is in place. + + This value starts from the end of the previous quantity range. + If there are no previous quantity ranges, + this value starts at 1. + type: + - integer + - 'null' + example: 1 + minimum: 1 + maximum: 65535 + minQuantity: + description: |- + Minimum permitted unit quantity. + If this value is `null`, no limit is in place. + type: + - integer + - 'null' + default: null + example: 1 + minimum: 1 + maximum: 65535 + PlanFormulaTiered: + title: Tiered + description: >- + Tiered pricing details. + + Use this formula to charge for units that are sold within defined + quantity ranges, or tiers. + + + For more information, see + [Tiered](https://www.rebilly.com/docs/settings/pricing-formulas/#tiered). + type: object + required: + - brackets + - formula + properties: + formula: + type: string + description: >- + Price formula that determines which algorithm is used to calculate + the invoice price based on the following factors: + + + - Quantity in the order. + This value may be variable if you are charging based on usage. + If you are not charging based on usage, + this value is determined when an order is created. + + - Price of the quantity range. + Some formulas set a price based on defined product quantity ranges. + + For more information, see [Price + formulas](https://www.rebilly.com/docs/billing/pricing-formulas/). + enum: + - tiered + brackets: + description: >- + Use this formula to charge for units that are sold within defined + quantity ranges, or tiers. + + If a customer buys a number of units that span more than one tier, + + the total cost is calculated based on the cost of the units in each + tier. + + + For example, a software company sells user licenses based on the + following: + + + Tier | Price per user | Max quantity | Description + + -----|----------------|--------------|------------ + + 1 | $40 | 3 | 1 to 3 user licenses + + 2 | $30 | 5 | 5 to 8 user licenses + + 3 | $15 | null | 9 or more user licenses + + + If a customer buys 10 user licenses, the pricing is as follows: + + - 3 licenses at tier 1 (3 x $40) is 120 + + - 5 licenses at tier 2 (5 x $30) is 150 + + - 2 licenses at tier 3 (2 x $15) is 30 + + + Total cost = $330 + + + Formula: `(price for the tier x quantity within the tier)` + `(price + for next tier x quantity in next tier)` + `...` = final price. + type: array + minItems: 1 + items: + type: object + properties: + price: + description: |- + Price of the quantity range. + If the value of this field is `0`, the product is free. + type: number + format: double + example: 99.95 + maxQuantity: + description: |- + Maximum permitted unit quantity. + If this value is `null`, no limit is in place. + + This value starts from the end of the previous quantity range. + If there are no previous quantity ranges, + this value starts at 1. + type: + - integer + - 'null' + example: 1 + minimum: 1 + maximum: 65535 + minQuantity: + description: |- + Minimum permitted unit quantity. + If this value is `null`, no limit is in place. + type: + - integer + - 'null' + default: null + example: 1 + minimum: 1 + maximum: 65535 + PlanFormulaVolume: + title: Volume + description: >- + Volume pricing details. + + Use this formula to charge for units that are sold in bulk, + + or volume ranges. In general, + + this formula means that a customer pays less per unit when they buy a + large volume of units. + + + For more information, see + [Volume](https://www.rebilly.com/docs/settings/pricing-formulas/#volume). + type: object + required: + - brackets + - formula + properties: + formula: + type: string + description: >- + Price formula that determines which algorithm is used to calculate + the invoice price based on the following factors: + + + - Quantity in the order. + This value may be variable if you are charging based on usage. + If you are not charging based on usage, + this value is determined when an order is created. + + - Price of the quantity range. + Some formulas set a price based on defined product quantity ranges. + + For more information, see [Price + formulas](https://www.rebilly.com/docs/billing/pricing-formulas/). + enum: + - volume + brackets: + description: >- + Use this formula to charge for units that are sold in bulk, or + volume ranges. + + This formula enables customers to pays less per unit when they buy a + large volume of units. + + Unit cost is based on the highest volume range, and does not include + lower volume range pricing. + + + For example, a company sells t-shirts in the following volume + ranges: + + + Price per unit| Max quantity | Description + + --------------|--------------|------------ + + $10 | 1 | 1 t-shirt + + $8 | 5 | 2 to 5 t-shirts + + $4 | null | 6 or more t-shirts + + + - If a customer buys 1 unit, they pay $10. + + - If a customer buys 3 units, they pay $24 per unit, which is $8 per + unit. + + - If a customer buys 10 units, they pay $40, which is $4 per unit. + type: array + minItems: 1 + items: + type: object + properties: + price: + description: |- + Price of the quantity range. + If the value of this field is `0`, the product is free. + type: number + format: double + example: 99.95 + maxQuantity: + description: |- + Maximum permitted unit quantity. + If this value is `null`, no limit is in place. + + This value starts from the end of the previous quantity range. + If there are no previous quantity ranges, + this value starts at 1. + type: + - integer + - 'null' + example: 1 + minimum: 1 + maximum: 65535 + minQuantity: + description: |- + Minimum permitted unit quantity. + If this value is `null`, no limit is in place. + type: + - integer + - 'null' + default: null + example: 1 + minimum: 1 + maximum: 65535 + PlanPriceFormula: + description: Pricing details. + type: object + discriminator: + propertyName: formula + mapping: + fixed-fee: '#/components/schemas/PlanFormulaFixedFee' + flat-rate: '#/components/schemas/PlanFormulaFlatRate' + stairstep: '#/components/schemas/PlanFormulaStairstep' + tiered: '#/components/schemas/PlanFormulaTiered' + volume: '#/components/schemas/PlanFormulaVolume' + oneOf: + - $ref: '#/components/schemas/PlanFormulaFixedFee' + - $ref: '#/components/schemas/PlanFormulaFlatRate' + - $ref: '#/components/schemas/PlanFormulaStairstep' + - $ref: '#/components/schemas/PlanFormulaTiered' + - $ref: '#/components/schemas/PlanFormulaVolume' + OneTimeSalePlan: + type: object + description: >- + Details of the one-time sale plan. Use this plan for non-recurring, + one-time, sales. + required: + - name + - currency + - productId + - pricing + properties: + name: + description: |- + Name of the plan. + This name is displayed on invoices and receipts. + type: string + maxLength: 255 + description: + type: string + description: |- + Plain-text description of the plan. + This field accepts plain-text only. + maxLength: 65535 + richDescription: + type: string + description: >- + Rich-text description of the plan. + + This field accepts rich text formatting, such as: bold, underline, + italic, and hyperlinks. + maxLength: 65535 + productId: + type: string + description: ID of the related product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + productOptions: + type: + - object + - 'null' + description: Name-value pairs that specify the product options. + additionalProperties: + type: string + example: + color: red + size: xxl + currency: + $ref: '#/components/schemas/CurrencyCode' + currencySign: + description: Currency sign. + readOnly: true + type: string + pricing: + $ref: '#/components/schemas/PlanPriceFormula' + setup: + type: + - object + - 'null' + description: Set up information of the plan. + required: + - price + properties: + price: + description: |- + Price of setting up the plan. + If your service charges a set up fee, specify it here. + To charge no set up fee, set this value to `0`. + type: number + format: double + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + isActive: + type: boolean + description: Specifies if the plan is active. + default: true + revision: + type: integer + readOnly: true + description: >- + Number of times the plan is modified. + + Compare this value with materialized subscription item revision + values. + isTrialOnly: + type: boolean + description: >- + Specifies if a plan is a trial that does not have recurring + instructions. + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - attachments + InvoiceTimeShift: + type: + - object + - 'null' + description: >- + Use invoice time shift to control the billing time. + + + Invoice time shift adjusts the invoice issue and due date when billing + must occur before the service period changes. + + + Use invoice time shift in conjunction with `billingTiming` to: + + + - Bill immediately when the service period starts. + + - Bill immediately after the service period ends. + + - Bill at an interval of time before the service period starts. + + - Bill at an interval of time after the service period starts. + + - Bill at an interval of time before the service period ends. + + - Bill at an interval of time after the service period ends. + properties: + issueTimeShift: + type: object + description: >- + Calculation instruction of the billing time. + + + This is used in conjunction with the service period anchor to + calculate + + the time at which the invoice is issued. For more information, see + + [Service period anchor, billing timing, and invoice time + shift](https://www.rebilly.com/docs/dev-docs/concepts/#service-period-anchor-and-billing-timing-and-invoice-time-shift). + required: + - chronology + - duration + - unit + properties: + chronology: + type: string + description: >- + Sequential order of the billing time relative to the start of + the service period. + enum: + - before + duration: + type: integer + description: Amount of time by which to move the invoice issue time or date. + minimum: 1 + unit: + description: Unit of time. + oneOf: + - $ref: '#/components/schemas/TimeUnit' + - $ref: '#/components/schemas/TimePluralUnit' + dueTimeShift: + type: object + required: + - duration + - unit + description: >- + Calculation instruction of the invoice due time. + + + This is used in conjunction with the billing anchor to calculate + when + + an invoice is due for payment. For more information, see + + [Service period anchor, billing timing, and invoice time + shift](https://www.rebilly.com/docs/dev-docs/concepts/#service-period-anchor-and-billing-timing-and-invoice-time-shift). + + + The sequential order of due time shift is always after the due date. + default: + duration: 1 + unit: hour + properties: + duration: + type: integer + description: Amount of time by which to move the invoice due time or date. + minimum: 1 + unit: + description: Unit of time. + oneOf: + - $ref: '#/components/schemas/TimeUnit' + - $ref: '#/components/schemas/TimePluralUnit' + SubscriptionOrderPlan: + description: >- + Details of the subscription order plan. Use this plan for subscriptions + or sales that reoccur over a period of time. + type: object + required: + - name + - currency + - productId + - pricing + - recurringInterval + properties: + name: + description: |- + Name of the plan. + This name is displayed on invoices and receipts. + type: string + maxLength: 255 + description: + type: string + description: |- + Plain-text description of the plan. + This field accepts plain-text only. + maxLength: 65535 + richDescription: + type: string + description: >- + Rich-text description of the plan. + + This field accepts rich text formatting, such as: bold, underline, + italic, and hyperlinks. + maxLength: 65535 + productId: + type: string + description: ID of the related product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + productOptions: + type: + - object + - 'null' + description: Name-value pairs that specify the product options. + additionalProperties: + type: string + example: + color: red + size: xxl + currency: + $ref: '#/components/schemas/CurrencyCode' + currencySign: + description: Currency sign. + readOnly: true + type: string + pricing: + $ref: '#/components/schemas/PlanPriceFormula' + setup: + type: + - object + - 'null' + description: Set up information of the plan. + required: + - price + properties: + price: + description: |- + Price of setting up the plan. + If your service charges a set up fee, specify it here. + To charge no set up fee, set this value to `0`. + type: number + format: double + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + isActive: + type: boolean + description: Specifies if the plan is active. + default: true + revision: + type: integer + readOnly: true + description: >- + Number of times the plan is modified. + + Compare this value with materialized subscription item revision + values. + isTrialOnly: + type: boolean + description: >- + Specifies if a plan is a trial that does not have recurring + instructions. + readOnly: true + recurringInterval: + description: |- + Service interval settings. + For a one-time sale, set this value to `null`. + type: object + properties: + periodAnchorInstruction: + type: + - string + - 'null' + unit: + description: Unit of time. + type: string + enum: + - day + - week + - month + - year + length: + description: Length of time. + type: integer + minimum: 1 + limit: + description: >- + Number of invoices the subscription order generates. + + If this value is `1`, only the initial order creation is + generated. + + + Example: For a 1 year contract that is billed monthly, + + where the `periodUnit` is `month` and the `periodDuration` is + `1`, + + set this field to `12`. + type: + - integer + - 'null' + minimum: 1 + maximum: 65535 + billingTiming: + type: string + default: prepaid + description: >- + Billing timing in relation to the service period. + + For `prepaid` plans the customer pays when the service period + starts, + + whereas, for `postpaid` plans, the customer pays when the + service period ends. + enum: + - prepaid + - postpaid + trial: + type: + - object + - 'null' + description: |- + Trial configuration setting. + If you do not want to offer a trial, set this value to `null`. + required: + - price + - period + properties: + price: + description: |- + Price of the trial. + For a free trial, set this value to `0`. + type: number + format: double + period: + type: object + description: Period information. + required: + - unit + - length + properties: + unit: + description: Unit of time. + type: string + enum: + - day + - week + - month + - year + length: + description: Length of time. + type: integer + minimum: 1 + meteredBilling: + type: + - object + - 'null' + required: + - strategy + description: >- + Use metered billing when an exact quantity is unknown. + + Report usage during a service period and charge customers + afterwards. + + Metered billing plans must be postpaid. + properties: + strategy: + type: string + enum: + - sum + - last + x-enumDescriptions: + sum: Total amount of reported invoice item quantity usage. + last: Last reported invoice item quantity usage. + min: + description: >- + Minimum quantity that is charged at the end of a service period + regardless of reported usage. + type: + - number + - 'null' + format: float + minimum: 0.01 + max: + description: >- + Maximum quantity that is charged at the end of a service period + regardless of reported usage. + type: + - number + - 'null' + format: float + minimum: 0.01 + invoiceTimeShift: + $ref: '#/components/schemas/InvoiceTimeShift' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - attachments + TrialOnlyPlan: + description: |- + Details of the trial-only plan. + Use this plan for limited-time product trials. + Trials may have a reduced fee, or may be free. + type: object + required: + - name + - currency + - productId + - pricing + - trial + properties: + name: + description: |- + Name of the plan. + This name is displayed on invoices and receipts. + type: string + maxLength: 255 + description: + type: string + description: |- + Plain-text description of the plan. + This field accepts plain-text only. + maxLength: 65535 + richDescription: + type: string + description: >- + Rich-text description of the plan. + + This field accepts rich text formatting, such as: bold, underline, + italic, and hyperlinks. + maxLength: 65535 + productId: + type: string + description: ID of the related product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + productOptions: + type: + - object + - 'null' + description: Name-value pairs that specify the product options. + additionalProperties: + type: string + example: + color: red + size: xxl + currency: + $ref: '#/components/schemas/CurrencyCode' + currencySign: + description: Currency sign. + readOnly: true + type: string + pricing: + $ref: '#/components/schemas/PlanPriceFormula' + setup: + type: + - object + - 'null' + description: Set up information of the plan. + required: + - price + properties: + price: + description: |- + Price of setting up the plan. + If your service charges a set up fee, specify it here. + To charge no set up fee, set this value to `0`. + type: number + format: double + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + isActive: + type: boolean + description: Specifies if the plan is active. + default: true + revision: + type: integer + readOnly: true + description: >- + Number of times the plan is modified. + + Compare this value with materialized subscription item revision + values. + isTrialOnly: + type: boolean + description: >- + Specifies if a plan is a trial that does not have recurring + instructions. + readOnly: true + trial: + type: object + description: Trial configuration settings. + required: + - price + - period + properties: + price: + description: |- + Price of setting up a trial. + If your service charges a fee for a trial, specify it here. + To charge no trial fee, set this value to `0`. + type: number + format: double + period: + type: object + description: Period information. + required: + - unit + - length + properties: + unit: + description: Unit of time. + type: string + enum: + - day + - week + - month + - year + length: + description: Length of time. + type: integer + minimum: 1 + invoiceTimeShift: + description: >- + Use invoice time shift to adjust the invoice issue and due date when + billing must occur before the service period changes. + + For example, rent must be paid in advance, + + so the invoice must be in advance of the billing date. + + For more information, see [Service period anchor, and billing + timing, and invoice time + shift](https://www.rebilly.com/docs/dev-docs/concepts/#service-period-anchor-and-billing-timing-and-invoice-time-shift). + $ref: '#/components/schemas/InvoiceTimeShift' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - attachments + Plan: + allOf: + - type: object + properties: + id: + type: string + description: ID of the plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + readOnly: true + - anyOf: + - $ref: '#/components/schemas/OneTimeSalePlan' + - $ref: '#/components/schemas/SubscriptionOrderPlan' + - $ref: '#/components/schemas/TrialOnlyPlan' + Product: + type: object + required: + - name + description: >- + Products are the items that your business sells. + + Products include digital goods, services, and physical goods. + + Products appear on invoice line items. + + If you set a tax category identifier, taxes are calculated at invoice + generation. + + If the product is shippable, shipping costs are calculated at invoice + generation. + + Pricing and variations are set within plans. + + For more information, see + [Plans](https://www.rebilly.com/docs/dev-docs/api/tag/Plans/). + properties: + id: + type: string + description: ID of the product. + readOnly: true + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + name: + description: Name of the product. + type: string + maxLength: 255 + example: Premium membership + unitLabel: + description: 'Unit label, such as per `seat` or per `unit`.' + type: string + maxLength: 50 + example: seat + default: unit + description: + description: Description of the product. + type: + - string + - 'null' + maxLength: 512 + requiresShipping: + description: |- + Specifies if the product requires shipping. + If this value is `true`, shipping calculations are applied. + type: boolean + example: false + default: false + options: + description: >- + Product options such as color, size, and so forth. + + Product option values are defined in plans. + + For more information, see + [Plans](https://www.rebilly.com/docs/dev-docs/api/tag/Plans/). + type: array + items: + type: string + taxCategoryId: + description: >- + Tax category of the product. + + For a complete list of supported tax categories, see [TaxJar sales + tax API + reference](https://developers.taxjar.com/api/reference/#get-list-tax-categories). + + If none of the tax categories from the list are applicable for your + product, use the general tax category `00000`. + type: + - string + - 'null' + example: '00000' + accountingCode: + description: Accounting code of the product. + type: + - string + - 'null' + example: '4010' + recognition: + type: + - object + - 'null' + properties: + debitAccountId: + description: ID of the debit journal account. + type: + - string + - 'null' + creditAccountId: + description: ID of the credit journal account. + type: + - string + - 'null' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + OriginalPlan: + type: object + description: >- + Use an existing plan without changes for the current order. + + + To create a new plan, see [Create a + plan](https://all-rebilly.redoc.ly/tag/Plans/operation/PostPlan). + required: + - id + properties: + id: + description: ID of the plan. + $ref: '#/components/schemas/ResourceId' + FlexiblePlan: + allOf: + - type: object + description: >- + Customize an existing plan for the current order. + + + To create a new plan, see [Create a + plan](https://all-rebilly.redoc.ly/tag/Plans/operation/PostPlan). + required: + - id + properties: + id: + type: string + description: ID of the plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + - anyOf: + - $ref: '#/components/schemas/OneTimeSalePlan' + - $ref: '#/components/schemas/SubscriptionOrderPlan' + - $ref: '#/components/schemas/TrialOnlyPlan' + Quote: + type: object + required: + - websiteId + - customerId + - items + properties: + id: + readOnly: true + description: ID of the quote. + type: string + maxLength: 50 + example: qt_0YV7DES3WPC5J8JD8QTVNZBZNZ + type: + description: >- + Specifies the type of quote. A quote can be a subscription or a + one-time purchase. + type: string + readOnly: true + enum: + - subscription-order + - one-time-order + status: + description: Status of the quote. + type: string + readOnly: true + enum: + - draft + - issued + - accepted + - rejected + - canceled + - expired + x-enumDescriptions: + draft: Quote can be edited. This quote cannot be sent to a customer. + issued: Quote cannot be edited. This quote can be sent to a customer. + accepted: Customer accepted the quote and created an order. + rejected: Customer rejected the quote. + canceled: Organization canceled the quote. + expired: Expired before customer or organization interaction. + websiteId: + $ref: '#/components/schemas/WebsiteId' + customerId: + $ref: '#/components/schemas/CustomerId' + orderId: + description: ID of the order. + readOnly: true + type: + - string + - 'null' + maxLength: 50 + example: ord_0YV7DES3WPC5J8JD8QTVNZBZNZ + items: + type: array + description: Items included in the quote. + minItems: 1 + items: + type: object + required: + - plan + - quantity + properties: + id: + description: ID of the quote item. + readOnly: true + type: string + example: qti_0YV7DES3WPC5J8JD8QTVNZBZNZ + quantity: + description: Number of product units in the specified plan. + type: integer + minimum: 1 + plan: + description: Plan details. + oneOf: + - $ref: '#/components/schemas/OriginalPlan' + - $ref: '#/components/schemas/FlexiblePlan' + description: + type: string + description: Description of the quote item. + example: Charge per approved transaction with DCC + default: '' + maxLength: 255 + priceDescription: + type: string + description: |- + Price description of the quote item. + This value is only used for metered billing items. + example: 50% of the markup for approved transactions + default: '' + maxLength: 255 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _embedded: + type: object + description: >- + Embedded objects that are requested by the `expand` query + parameter. + readOnly: true + properties: + product: + type: object + deliveryAddress: + description: Delivery address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + billingAddress: + description: Billing address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + invoicePreview: + type: object + description: Preview of the quote invoice. + readOnly: true + properties: + currency: + description: Currency of the invoice. + $ref: '#/components/schemas/CurrencyCode' + initialAmounts: + type: object + description: Total amounts of the initial invoice. + properties: + amount: + description: Amount of the invoice. + type: number + x-type: Money + format: double + subtotalAmount: + description: Subtotal amount of the invoice. + type: number + x-type: Money + format: double + discountAmount: + description: Discount amount that is applied to the invoice. + type: number + x-type: Money + format: double + shippingAmount: + description: Shipping amount that is applied to the invoice. + type: number + x-type: Money + format: double + taxAmount: + description: Tax amount that is applied to the invoice. + type: number + x-type: Money + format: double + recurringAmounts: + type: + - object + - 'null' + description: >- + Total amounts of the recurring invoice. + + This includes recurring items only. + + If the quote does not have recurring items, the value of this + field is `null`. + properties: + amount: + description: Amount of the invoice. + type: number + x-type: Money + format: double + subtotalAmount: + description: Subtotal amount of the invoice. + type: number + x-type: Money + format: double + discountAmount: + description: Discount amount that is applied to the invoice. + type: number + x-type: Money + format: double + shippingAmount: + description: Shipping amount that is applied to the invoice. + type: number + x-type: Money + format: double + taxAmount: + description: Tax amount that is applied to the invoice. + type: number + x-type: Money + format: double + items: + type: array + description: Invoice items. + items: + type: object + properties: + quoteItemId: + description: ID of the related quote item. + type: string + example: qti_0YV7DES3WPC5J8JD8QTVNZBZNZ + type: + type: string + description: Type of the invoice item. + x-basic: true + enum: + - debit + - credit + name: + description: Name of the invoice item. + type: string + maxLength: 1000 + description: + description: Description of the invoice item. + type: string + example: Charge per approved transaction with DCC + maxLength: 255 + priceDescription: + type: string + description: Price description of the invoice item. + example: 50% of the markup for approved transactions + maxLength: 255 + unitPrice: + description: Unit price of the invoice item. + type: + - number + - 'null' + format: double + quantity: + description: Quantity of the invoice item. + type: integer + period: + description: Date interval of the invoice item. + type: + - string + - 'null' + setupUnitPrice: + description: Unit price of the invoice item. + type: + - number + - 'null' + format: double + trialUnitPrice: + description: Unit price of the invoice item. + type: + - number + - 'null' + format: double + trialPeriod: + description: Date interval of the invoice item trial. + type: + - string + - 'null' + taxAmount: + description: Invoice item tax. + type: + - number + - 'null' + format: double + setupTaxAmount: + description: Tax amount of the setup that is applied to the invoice. + type: + - number + - 'null' + format: double + trialTaxAmount: + description: Tax amount of the trial that is applied to the invoice. + type: + - number + - 'null' + format: double + paymentTerms: + description: Payment terms for the customer which are displayed on the quote. + type: string + expirationTime: + description: >- + Date and time when the quote expires. The default expiration time is + one month from the time the quote is issued. + + In a `draft` state, this field may be `null`. + type: + - string + - 'null' + format: date-time + issuedTime: + description: Date and time when the quote is issued. + type: + - string + - 'null' + readOnly: true + format: date-time + acceptedTime: + description: Date and time when the quote is accepted. + type: + - string + - 'null' + readOnly: true + format: date-time + rejectedTime: + description: Date and time when the quote is rejected. + type: + - string + - 'null' + readOnly: true + format: date-time + canceledTime: + description: Date and time when the quote is canceled. + type: + - string + - 'null' + readOnly: true + format: date-time + redirectUrl: + description: >- + URL to redirect the customer to when a quote is rejected. The + default value is the website URL. + type: string + format: uri + signature: + type: object + properties: + showWrittenSignatureLines: + type: boolean + default: false + description: Specifies whether to show written signature lines. + organizationPrintedName: + type: + - string + - 'null' + description: Printed name of the organization. + default: null + shipping: + $ref: '#/components/schemas/Shipping' + tax: + $ref: '#/components/schemas/Taxes' + couponIds: + type: + - array + - 'null' + description: >- + List of coupons to redeem on the customer and apply to the order + when the quote is accepted. + + + For more information, see + [Coupons](https://www.rebilly.com/docs/settings/coupons-and-discounts/). + items: + type: string + description: Coupon ID. + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - quoteAcceptanceFormUrl + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + website: + type: object + order: + type: object + PatchQuote: + type: object + description: Patch quote object. + properties: + tax: + $ref: '#/components/schemas/Taxes' + QuoteTimeline: + type: object + properties: + id: + type: string + description: ID of the timeline message. + readOnly: true + maxLength: 50 + example: tmln_0YV8Q9WEF5DTA8HFXS27D1G6GC + type: + description: Type of timeline message. + type: string + readOnly: true + enum: + - quote-created + - quote-issued + - quote-accepted + - quote-rejected + - quote-canceled + - quote-recalled + - quote-updated + - quote-expired + - quote-order-attached + triggeredBy: + description: 'Specifies who, or what, triggered the timeline message.' + type: string + readOnly: true + enum: + - rebilly + - app + - direct-api + message: + description: Contents of the timeline message. + type: string + extraData: + $ref: '#/components/schemas/TimelineExtraData' + occurredTime: + description: Date and time when the timeline message occurred. + readOnly: true + $ref: '#/components/schemas/ServerTimestamp' + _links: + $ref: '#/components/schemas/SelfLink' + ReadyToPayAmount: + type: object + title: With amount + required: + - websiteId + - currency + - amount + - riskMetadata + properties: + websiteId: + $ref: '#/components/schemas/WebsiteId' + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Amount to pay. + type: number + format: double + billingAddress: + description: Billing address. + writeOnly: true + $ref: '#/components/schemas/ContactObject' + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + ReadyToPayItems: + type: object + title: With items + required: + - websiteId + - items + - riskMetadata + properties: + websiteId: + $ref: '#/components/schemas/WebsiteId' + items: + type: array + minItems: 1 + items: + type: object + required: + - planId + - quantity + properties: + planId: + type: string + description: ID of the plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + quantity: + description: Number of product units in the specified plan. + type: integer + billingAddress: + description: Billing address. + writeOnly: true + $ref: '#/components/schemas/ContactObject' + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + ReadyToPay: + type: object + oneOf: + - $ref: '#/components/schemas/ReadyToPayAmount' + - $ref: '#/components/schemas/ReadyToPayItems' + PostReadyToPay: + allOf: + - type: object + properties: + customerId: + $ref: '#/components/schemas/CustomerId' + - $ref: '#/components/schemas/ReadyToPay' + Country: + description: >- + ISO 3166 alpha-2 country code. For examples, see + [ISO.org](https://www.iso.org/obp/ui/#search/code/). + type: string + pattern: '^[A-Z]{2}$' + example: US + ApplePayFeature: + type: object + title: Apple Pay Digital Wallet + properties: + name: + type: string + description: Name of the feature. + enum: + - Apple Pay + displayName: + description: >- + String of 64 or fewer UTF-8 characters containing the canonical name + for your store, + + that is suitable for display. + + Do not localize the name. + type: string + example: Test Merchant + country: + $ref: '#/components/schemas/Country' + GooglePayFeature: + type: object + title: Google Pay Digital Wallet + properties: + name: + type: string + description: Name of the feature. + enum: + - Google Pay + merchantName: + description: Merchant name in Google Pay. + type: string + example: Test Merchant + merchantOrigin: + description: Merchant origin in Google Pay. The fully qualified domain name. + type: string + example: www.example.com + country: + $ref: '#/components/schemas/Country' + PaymentCardFeature: + type: object + discriminator: + propertyName: name + mapping: + Apple Pay: '#/components/schemas/ApplePayFeature' + Google Pay: '#/components/schemas/GooglePayFeature' + oneOf: + - $ref: '#/components/schemas/ApplePayFeature' + - $ref: '#/components/schemas/GooglePayFeature' + ReadyToPayMethodFilters: + type: array + description: >- + For the method to be applicable, one or more of the following filters + must match. + + If no filters are sent, no restrictions are applied. + + For more information, see [Using + filters](https://all-rebilly.redoc.ly/#section/Using-filter-with-collections). + items: + type: string + ReadyToPayPaymentCardMethod: + type: object + title: Payment card + required: + - method + properties: + method: + type: string + description: Payment method. + enum: + - payment-card + feature: + description: |- + Specific feature of this method. + For example, a digital wallet. + If the method does not have any features, this value is null. + oneOf: + - $ref: '#/components/schemas/PaymentCardFeature' + - type: 'null' + brands: + type: array + description: List of supported brands. + minItems: 1 + items: + $ref: '#/components/schemas/PaymentCardBrand' + filters: + $ref: '#/components/schemas/ReadyToPayMethodFilters' + AchPlaidFeature: + type: object + title: Plaid + required: + - name + - linkToken + - expirationTime + properties: + name: + type: string + description: Name of the feature. + enum: + - Plaid + linkToken: + type: string + description: Plaid `linkToken` for frontend integrations. + expirationTime: + type: string + format: date-time + description: Date and time when the `linkToken` expires. + ReadyToPayAchMethod: + type: object + title: ACH + required: + - method + properties: + method: + type: string + description: Payment method. + enum: + - ach + feature: + description: |- + Specific feature of this method. + For example, a digital wallet or a processor. + If the method does not have any features, this value is null. + oneOf: + - $ref: '#/components/schemas/AchPlaidFeature' + - type: 'null' + filters: + $ref: '#/components/schemas/ReadyToPayMethodFilters' + ReadyToPayGenericMethod: + type: object + title: Generic + required: + - method + properties: + method: + $ref: '#/components/schemas/AlternativePaymentMethods' + filters: + $ref: '#/components/schemas/ReadyToPayMethodFilters' + PayPalBillingAgreementFeature: + type: object + title: Billing agreement + required: + - name + - paypalMerchantId + - paypalClientId + - billingAgreementToken + - expirationTime + properties: + name: + type: string + description: Name of the feature. + enum: + - PayPal billing agreement + paypalMerchantId: + type: string + description: ID of the PayPal merchant. + paypalClientId: + type: string + description: ID of the PayPal client. + billingAgreementToken: + type: string + description: PayPal billing agreement token. + expirationTime: + type: string + format: date-time + description: Date and time when the billing agreement token expires. + ReadyToPayPayPalMethod: + type: object + title: PayPal + required: + - method + properties: + method: + type: string + description: Payment method. + enum: + - paypal + feature: + description: Specific features of PayPal. + oneOf: + - $ref: '#/components/schemas/PayPalBillingAgreementFeature' + - type: 'null' + filters: + $ref: '#/components/schemas/ReadyToPayMethodFilters' + KlarnaFeature: + type: object + title: Klarna + required: + - name + - clientToken + - sessionId + properties: + name: + type: string + description: Name of the feature. + enum: + - Klarna + clientToken: + type: string + description: Klarna client token. + sessionId: + type: string + description: Klarna session token. + ReadyToPayKlarnaMethod: + type: object + title: Klarna + required: + - method + properties: + method: + type: string + description: Payment method. + enum: + - Klarna + feature: + description: |- + Specific feature of this method. + For example, a digital wallet or a processor. + If the method does not have any features, this value is null. + oneOf: + - $ref: '#/components/schemas/KlarnaFeature' + - type: 'null' + filters: + $ref: '#/components/schemas/ReadyToPayMethodFilters' + ReadyToPayMethods: + type: object + discriminator: + propertyName: method + mapping: + payment-card: '#/components/schemas/ReadyToPayPaymentCardMethod' + ach: '#/components/schemas/ReadyToPayAchMethod' + cash: '#/components/schemas/ReadyToPayGenericMethod' + check: '#/components/schemas/ReadyToPayGenericMethod' + paypal: '#/components/schemas/ReadyToPayPayPalMethod' + AdvCash: '#/components/schemas/ReadyToPayGenericMethod' + Alfa-click: '#/components/schemas/ReadyToPayGenericMethod' + Alipay: '#/components/schemas/ReadyToPayGenericMethod' + AstroPay Card: '#/components/schemas/ReadyToPayGenericMethod' + AstroPay-GO: '#/components/schemas/ReadyToPayGenericMethod' + BankReferenced: '#/components/schemas/ReadyToPayGenericMethod' + bank-transfer: '#/components/schemas/ReadyToPayGenericMethod' + bank-transfer-2: '#/components/schemas/ReadyToPayGenericMethod' + bank-transfer-3: '#/components/schemas/ReadyToPayGenericMethod' + bank-transfer-4: '#/components/schemas/ReadyToPayGenericMethod' + bank-transfer-5: '#/components/schemas/ReadyToPayGenericMethod' + bank-transfer-6: '#/components/schemas/ReadyToPayGenericMethod' + bank-transfer-7: '#/components/schemas/ReadyToPayGenericMethod' + bank-transfer-8: '#/components/schemas/ReadyToPayGenericMethod' + bank-transfer-9: '#/components/schemas/ReadyToPayGenericMethod' + Baloto: '#/components/schemas/ReadyToPayGenericMethod' + Beeline: '#/components/schemas/ReadyToPayGenericMethod' + Belfius-direct-net: '#/components/schemas/ReadyToPayGenericMethod' + bitcoin: '#/components/schemas/ReadyToPayGenericMethod' + Bizum: '#/components/schemas/ReadyToPayGenericMethod' + Boleto: '#/components/schemas/ReadyToPayGenericMethod' + cash-deposit: '#/components/schemas/ReadyToPayGenericMethod' + CASHlib: '#/components/schemas/ReadyToPayGenericMethod' + CashToCode: '#/components/schemas/ReadyToPayGenericMethod' + China UnionPay: '#/components/schemas/ReadyToPayGenericMethod' + Cleo: '#/components/schemas/ReadyToPayGenericMethod' + CODVoucher: '#/components/schemas/ReadyToPayGenericMethod' + Conekta-oxxo: '#/components/schemas/ReadyToPayGenericMethod' + Cupon-de-pagos: '#/components/schemas/ReadyToPayGenericMethod' + cryptocurrency: '#/components/schemas/ReadyToPayGenericMethod' + domestic-cards: '#/components/schemas/ReadyToPayGenericMethod' + echeck: '#/components/schemas/ReadyToPayGenericMethod' + ecoPayz: '#/components/schemas/ReadyToPayGenericMethod' + ecoVoucher: '#/components/schemas/ReadyToPayGenericMethod' + Efecty: '#/components/schemas/ReadyToPayGenericMethod' + EPS: '#/components/schemas/ReadyToPayGenericMethod' + ePay.bg: '#/components/schemas/ReadyToPayGenericMethod' + eZeeWallet: '#/components/schemas/ReadyToPayGenericMethod' + FasterPay: '#/components/schemas/ReadyToPayGenericMethod' + Flexepin: '#/components/schemas/ReadyToPayGenericMethod' + Giropay: '#/components/schemas/ReadyToPayGenericMethod' + Gpaysafe: '#/components/schemas/ReadyToPayGenericMethod' + Google Pay: '#/components/schemas/ReadyToPayGenericMethod' + iDebit: '#/components/schemas/ReadyToPayGenericMethod' + iDEAL: '#/components/schemas/ReadyToPayGenericMethod' + ING-homepay: '#/components/schemas/ReadyToPayGenericMethod' + INOVAPAY-pin: '#/components/schemas/ReadyToPayGenericMethod' + INOVAPAY-wallet: '#/components/schemas/ReadyToPayGenericMethod' + InstaDebit: '#/components/schemas/ReadyToPayGenericMethod' + instant-bank-transfer: '#/components/schemas/ReadyToPayGenericMethod' + InstantPayments: '#/components/schemas/ReadyToPayGenericMethod' + Interac: '#/components/schemas/ReadyToPayGenericMethod' + Interac-online: '#/components/schemas/ReadyToPayGenericMethod' + Interac-eTransfer: '#/components/schemas/ReadyToPayGenericMethod' + invoice: '#/components/schemas/ReadyToPayGenericMethod' + iWallet: '#/components/schemas/ReadyToPayGenericMethod' + Jeton: '#/components/schemas/ReadyToPayGenericMethod' + jpay: '#/components/schemas/ReadyToPayGenericMethod' + Khelocard: '#/components/schemas/ReadyToPayGenericMethod' + Klarna: '#/components/schemas/ReadyToPayKlarnaMethod' + KNOT: '#/components/schemas/ReadyToPayGenericMethod' + loonie: '#/components/schemas/ReadyToPayGenericMethod' + Matrix: '#/components/schemas/ReadyToPayGenericMethod' + MaxiCash: '#/components/schemas/ReadyToPayGenericMethod' + Megafon: '#/components/schemas/ReadyToPayGenericMethod' + MiFinity-eWallet: '#/components/schemas/ReadyToPayGenericMethod' + miscellaneous: '#/components/schemas/ReadyToPayGenericMethod' + Bancontact: '#/components/schemas/ReadyToPayGenericMethod' + Bancontact-mobile: '#/components/schemas/ReadyToPayGenericMethod' + MTS: '#/components/schemas/ReadyToPayGenericMethod' + MuchBetter: '#/components/schemas/ReadyToPayGenericMethod' + Multibanco: '#/components/schemas/ReadyToPayGenericMethod' + Neosurf: '#/components/schemas/ReadyToPayGenericMethod' + Netbanking: '#/components/schemas/ReadyToPayGenericMethod' + Neteller: '#/components/schemas/ReadyToPayGenericMethod' + Nordea-Solo: '#/components/schemas/ReadyToPayGenericMethod' + OchaPay: '#/components/schemas/ReadyToPayGenericMethod' + online-bank-transfer: '#/components/schemas/ReadyToPayGenericMethod' + Onlineueberweisen: '#/components/schemas/ReadyToPayGenericMethod' + oriental-wallet: '#/components/schemas/ReadyToPayGenericMethod' + OXXO: '#/components/schemas/ReadyToPayGenericMethod' + P24: '#/components/schemas/ReadyToPayGenericMethod' + Pagadito: '#/components/schemas/ReadyToPayGenericMethod' + PagoEffectivo: '#/components/schemas/ReadyToPayGenericMethod' + Pagsmile-deposit-express: '#/components/schemas/ReadyToPayGenericMethod' + Pagsmile-lottery: '#/components/schemas/ReadyToPayGenericMethod' + PayCash: '#/components/schemas/ReadyToPayGenericMethod' + Payeer: '#/components/schemas/ReadyToPayGenericMethod' + PaymentAsia-crypto: '#/components/schemas/ReadyToPayGenericMethod' + Paymero: '#/components/schemas/ReadyToPayGenericMethod' + Perfect-money: '#/components/schemas/ReadyToPayGenericMethod' + Piastrix: '#/components/schemas/ReadyToPayGenericMethod' + plaid-account: '#/components/schemas/ReadyToPayGenericMethod' + PayTabs: '#/components/schemas/ReadyToPayGenericMethod' + Paysafecard: '#/components/schemas/ReadyToPayGenericMethod' + Paysafecash: '#/components/schemas/ReadyToPayGenericMethod' + Pay4Fun: '#/components/schemas/ReadyToPayGenericMethod' + Paynote: '#/components/schemas/ReadyToPayGenericMethod' + PinPay: '#/components/schemas/ReadyToPayGenericMethod' + phone: '#/components/schemas/ReadyToPayGenericMethod' + PhonePe: '#/components/schemas/ReadyToPayGenericMethod' + POLi: '#/components/schemas/ReadyToPayGenericMethod' + PostFinance-card: '#/components/schemas/ReadyToPayGenericMethod' + PostFinance-e-finance: '#/components/schemas/ReadyToPayGenericMethod' + QIWI: '#/components/schemas/ReadyToPayGenericMethod' + QPay: '#/components/schemas/ReadyToPayGenericMethod' + QQPay: '#/components/schemas/ReadyToPayGenericMethod' + rapyd-checkout: '#/components/schemas/ReadyToPayGenericMethod' + Resurs: '#/components/schemas/ReadyToPayGenericMethod' + SafetyPay: '#/components/schemas/ReadyToPayGenericMethod' + SEPA: '#/components/schemas/ReadyToPayGenericMethod' + Skrill: '#/components/schemas/ReadyToPayGenericMethod' + Skrill Rapid Transfer: '#/components/schemas/ReadyToPayGenericMethod' + SMSVoucher: '#/components/schemas/ReadyToPayGenericMethod' + Sofort: '#/components/schemas/ReadyToPayGenericMethod' + SparkPay: '#/components/schemas/ReadyToPayGenericMethod' + swift-dbt: '#/components/schemas/ReadyToPayGenericMethod' + Tele2: '#/components/schemas/ReadyToPayGenericMethod' + Terminaly-RF: '#/components/schemas/ReadyToPayGenericMethod' + ToditoCash-card: '#/components/schemas/ReadyToPayGenericMethod' + Trustly: '#/components/schemas/ReadyToPayGenericMethod' + UPayCard: '#/components/schemas/ReadyToPayGenericMethod' + UPI: '#/components/schemas/ReadyToPayGenericMethod' + USD-coin: '#/components/schemas/ReadyToPayGenericMethod' + VCreditos: '#/components/schemas/ReadyToPayGenericMethod' + VenusPoint: '#/components/schemas/ReadyToPayGenericMethod' + voucher: '#/components/schemas/ReadyToPayGenericMethod' + voucher-2: '#/components/schemas/ReadyToPayGenericMethod' + voucher-3: '#/components/schemas/ReadyToPayGenericMethod' + voucher-4: '#/components/schemas/ReadyToPayGenericMethod' + Webmoney: '#/components/schemas/ReadyToPayGenericMethod' + Webpay: '#/components/schemas/ReadyToPayGenericMethod' + Webpay-2: '#/components/schemas/ReadyToPayGenericMethod' + Webpay Card: '#/components/schemas/ReadyToPayGenericMethod' + WeChat Pay: '#/components/schemas/ReadyToPayGenericMethod' + XPay-P2P: '#/components/schemas/ReadyToPayGenericMethod' + XPay-QR: '#/components/schemas/ReadyToPayGenericMethod' + Yandex-money: '#/components/schemas/ReadyToPayGenericMethod' + Zotapay: '#/components/schemas/ReadyToPayGenericMethod' + Zimpler: '#/components/schemas/ReadyToPayGenericMethod' + anyOf: + - $ref: '#/components/schemas/ReadyToPayPaymentCardMethod' + - $ref: '#/components/schemas/ReadyToPayAchMethod' + - $ref: '#/components/schemas/ReadyToPayGenericMethod' + - $ref: '#/components/schemas/ReadyToPayPayPalMethod' + - $ref: '#/components/schemas/ReadyToPayKlarnaMethod' + ServicePeriodAnchorInstruction: + type: object + description: |- + Instruction for calculating the service period anchor. + + The service period anchor is used, in conjunction with the subscription + start time, to calculate when the service period starts and ends. + discriminator: + propertyName: method + mapping: + day-of-month: '#/components/schemas/SchedulingMethodDayOfMonth' + day-of-week: '#/components/schemas/SchedulingMethodDayOfWeek' + immediately: '#/components/schemas/SchedulingMethodImmediately' + default: + method: immediately + anyOf: + - $ref: '#/components/schemas/SchedulingMethodDayOfMonth' + - $ref: '#/components/schemas/SchedulingMethodDayOfWeek' + - $ref: '#/components/schemas/SchedulingMethodImmediately' + OrderItem: + type: object + required: + - plan + properties: + id: + type: string + description: ID of the order item. + readOnly: true + maxLength: 50 + example: ord_itm_0YVJ6172Q2CXW9VP7KJMKHCMMX + planId: + description: ID of the plan. + deprecated: true + x-basic: true + type: string + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + quantity: + description: Number of product units in the specified plan. + type: integer + plan: + description: Details of the plan. + anyOf: + - $ref: '#/components/schemas/OriginalPlan' + - $ref: '#/components/schemas/FlexiblePlan' + usageLimits: + type: object + description: >- + Specifies the soft and hard usage limit thresholds for an item with + a metered billing plan. + + This value is ignored when the plan is not metered. + properties: + softLimit: + type: object + description: >- + Defines thresholds for notification purposes. + + For example, to notify the customer that their usage is near the + hard limit of their metered billing plan. + + This notification informs the customer so that they can upgrade + their plan before the hard limit is reached. + + When the reported usage reaches 75%, 90%, and 100% of the + configured limit a specific event is fired. + + To notify the customer, a webhook and notification can be + configured for this event. + + This field is useful for accounting and customer success + purposes. + + The usage of metered billing plans can still be reported if the + soft limit is reached. + minProperties: 1 + maxProperties: 1 + properties: + quantity: + type: integer + description: Usage limit quantity. + minimum: 1 + amount: + type: number + format: double + description: Usage limit amount in the currency of the order. + minimum: 0.01 + hardLimit: + type: object + description: >- + Defines a limit where the customer can no longer use the + service. + + Hard limits are used in addition to soft limits. + + When a soft limit is reached, + + a customer may receive a notification + + but the service can still be provided up to the hard limit value + so that the customer can upgrade their plan. + + When the reported usage reaches the configured limit, + + a specific event is triggered. + + To notify the customer in the merchant system, + + or block a service, + + a webhook and notification can be configured for this event. + + When the total usage reaches the hard limit quantity, + + or amount values, + + metered billing plan usages can no longer be reported. + minProperties: 1 + maxProperties: 1 + properties: + quantity: + type: integer + description: Usage limit quantity. + minimum: 1 + amount: + type: number + format: double + description: Usage limit amount in the currency of the order. + minimum: 0.01 + revision: + type: integer + readOnly: true + description: >- + Revision number that increments with each overriding change to this + specific plan item. + isModified: + type: boolean + readOnly: true + description: Specifies if the plan information is modified for this subscription. + isGrandfathered: + type: boolean + readOnly: true + description: >- + Specifies if the current plan revision number is greater than the + plan item revision number. + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + product: + type: object + SubscriptionOrder: + type: object + required: + - orderType + - customerId + - websiteId + - items + properties: + id: + type: string + description: ID of the order. + readOnly: true + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + orderType: + description: |- + Specifies the type of order. + An order may be a subscription or a one-time purchase. + type: string + x-basic: true + enum: + - subscription-order + customerId: + $ref: '#/components/schemas/CustomerId' + renewalReminderTime: + description: Date and time when the renewal reminder event triggers. + type: + - string + - 'null' + format: date-time + readOnly: true + renewalReminderNumber: + description: Number of triggered renewal reminder events. + type: + - integer + - 'null' + readOnly: true + trialReminderTime: + description: Date and time when the trial reminder event triggers. + type: + - string + - 'null' + format: date-time + readOnly: true + trialReminderNumber: + description: Number of triggered trial reminder events. + type: + - integer + - 'null' + readOnly: true + abandonReminderTime: + description: Date and time when the abandon order reminder event triggers. + type: + - string + - 'null' + format: date-time + readOnly: true + abandonReminderNumber: + description: Number of abandon order reminder events that are triggered. + type: + - integer + - 'null' + readOnly: true + organizationId: + readOnly: true + allOf: + - $ref: '#/components/schemas/OrganizationId' + status: + description: >- + Status of the subscription service. + + A subscription starts in the `pending` status, and becomes `active` + when the service period begins. + type: string + readOnly: true + enum: + - pending + - active + - abandoned + - canceled + - churned + - paused + - voided + - completed + - trial-ended + inTrial: + description: Specifies if the subscription is currently in a trial period. + type: boolean + readOnly: true + trial: + type: object + description: >- + Trial details. + + To use plan defaults do not send the `trial` key, or send a `null` + value. + properties: + enabled: + description: |- + Specifies if there is a trial for this subscription. + Plans without trial prices are free trials. + type: boolean + endTime: + description: >- + Time and date when the trial ends. + + If a trial is enabled on this subscription, a value must be + provided. + type: + - string + - 'null' + format: date-time + isTrialOnly: + description: |- + Specifies if a subscription ends after a trial period. + If this value is `true`, recurring settings are ignored. + type: boolean + default: false + invoiceTimeShift: + description: |- + Shifts issue time and due time of invoices for this subscription. + + This setting overrides plan settings. + To use plan settings, set this value to `null`. + + To use multiple plans in one subscription, + all plans must have the same billing period, + this property enables the customer to subscribe to different plans. + example: null + oneOf: + - $ref: '#/components/schemas/InvoiceTimeShift' + - type: 'null' + recurringInterval: + type: + - object + - 'null' + description: |- + Recurring interval to override plan settings. + To use plan settings, set this value to `null`. + + To use multiple plans in one subscription, + all plans must have the same recurring period length. + example: null + properties: + periodAnchorInstruction: + $ref: '#/components/schemas/ServicePeriodAnchorInstruction' + autopay: + description: >- + Specifies if payment attempts are made automatically. + + If autopay is enabled, the payment is retrieved from the customer on + the renewal date using the payment instrument that is set at + `paymentInstrumentId`, + + or the default payment instrument on the subscription. + type: boolean + default: true + startTime: + description: |- + Date and time when the subscription starts. + If this value is `null`, the current time is used. + This value cannot be more than one service period in the past. + type: + - string + - 'null' + x-sortable: true + x-basic: true + example: null + format: date-time + endTime: + description: Date and time when the subscription ends. + x-sortable: true + type: + - string + - 'null' + format: date-time + readOnly: true + renewalTime: + description: Date and time when the subscription renews. + type: + - string + - 'null' + x-sortable: true + x-basic: true + format: date-time + rebillNumber: + description: Current billing period number. + type: + - integer + - 'null' + readOnly: true + x-sortable: true + lineItems: + description: >- + Subscription line items which queue until the next renewal (or + interim) invoice is issued for the subscription. + readOnly: true + type: array + deprecated: true + items: + type: object + description: >- + Invoice line item. Use `isInterim` property of upcoming invoice + items instead. + deprecated: true + properties: + type: + description: Type of invoice line item. + type: string + enum: + - debit + - credit + description: + description: Description of the line item. + type: string + maxLength: 1000 + unitPriceAmount: + description: Unit price of the line item. + type: number + format: double + example: 49.95 + unitPriceCurrency: + $ref: '#/components/schemas/CurrencyCode' + quantity: + description: Quantity of the line item. + type: integer + example: 1 + periodStartTime: + description: Date and time when the period begins for this item. + type: string + format: date-time + periodEndTime: + description: Date and time when the period ends for this item. + type: string + format: date-time + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + lineItemSubtotal: + type: object + readOnly: true + description: |- + Subtotal of line items in this subscription (signed value). + If credits exceed debits, this value is a negative number. + properties: + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + type: number + x-type: Money + x-sortable: true + description: Amount of the subtotal. + format: double + example: 49.95 + paymentInstrumentId: + type: + - string + - 'null' + description: >- + ID of the payment instrument to use for autopay. + + If this value is not provided, or if the payment instrument is + inactive, + + the customer's default payment instrument is used. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + billingStatus: + description: >- + Billing status of the most recent invoice. + + This value may help you to determine if you should change the + service status of the service, + + such as suspending the service. + type: string + readOnly: true + enum: + - draft + - unpaid + - past-due + - abandoned + - paid + - voided + - refunded + - disputed + - partially-refunded + - partially-paid + websiteId: + x-sortable: true + x-basic: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + currency: + description: Currency of the order. + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + initialInvoiceId: + description: ID of the initial invoice. + readOnly: true + type: + - string + - 'null' + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + recentInvoiceId: + description: |- + ID of the most recently issued invoice. + The invoice might not be `paid` yet. + readOnly: true + type: + - string + - 'null' + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + items: + description: Details of items in the order. + type: array + minItems: 1 + items: + $ref: '#/components/schemas/OrderItem' + deliveryAddress: + description: Delivery address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + billingAddress: + description: Billing address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + activationTime: + description: Date and time when the order is activated. + x-sortable: true + type: + - string + - 'null' + format: date-time + readOnly: true + voidTime: + description: Date and time when the order is voided. + type: + - string + - 'null' + format: date-time + readOnly: true + abandonTime: + type: + - string + - 'null' + description: >- + Date and time when the pending order is automatically abandoned. + + If this value is not passed during order creation, + + a [pending order + TTL](https://all-rebilly.redoc.ly/tag/Organizations/operation/PatchOrganization/#!path=settings/billing/pendingOrderTtl&t=request) + setting is used to calculate the value. + format: date-time + couponIds: + type: + - array + - 'null' + description: >- + List of coupons to redeem on the customer and restrict to this + order. + + + For more information, see + [Coupons](https://www.rebilly.com/docs/settings/coupons-and-discounts/). + + + This parameter uses the following logic: + + + - If this parameter is not supplied, applied coupons are not + changed. + + - If an empty array is supplied, all applied coupon redemptions are + canceled. + + - If a list of coupons is supplied, unapplied coupons in the list + are applied. + Coupons that have already been applied do not change state. + Applied coupons that are not supplied in list are canceled. + + If the list of applied coupons on a pending order is changed by this + parameter during an order update, the invoice for the order is + reissued. + writeOnly: true + items: + type: string + description: ID of the coupon. + poNumber: + description: Purchase order number displayed on the issued invoices. + type: + - string + - 'null' + example: PO123456 + shipping: + $ref: '#/components/schemas/Shipping' + notes: + description: Notes for the customer displayed on the order invoice. + type: string + canceledBy: + description: Specifies who initiated the cancellation. + type: + - string + - 'null' + readOnly: true + enum: + - merchant + - customer + - rebilly + - null + cancelCategory: + description: Category of the cancellation. + type: + - string + - 'null' + readOnly: true + enum: + - billing-failure + - did-not-use + - did-not-want + - missing-features + - bugs-or-problems + - do-not-remember + - risk-warning + - contract-expired + - too-expensive + - never-started + - switched-plan + - other + - null + cancelDescription: + description: Description of the cancellation reason in free form. + type: + - string + - 'null' + readOnly: true + maxLength: 255 + revision: + description: >- + Number of times the order data has been modified. + + + The revision is useful when analyzing webhook data to determine if + the + + change takes precedence over the current representation. + type: integer + readOnly: true + riskMetadata: + type: + - object + - 'null' + example: null + description: >- + Risk metadata. + + If this value is `null`, this field uses risk metadata that is + captured when creating the payment token. + allOf: + - $ref: '#/components/schemas/RiskMetadata' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - website + - customer + - initialInvoice + - recentInvoice + - approvalUrl + - attachments + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + recentInvoice: + type: object + initialInvoice: + type: object + customer: + type: object + website: + type: object + leadSource: + type: object + shippingRate: + type: object + paymentInstrument: + type: object + upcomingInvoice: + type: object + OneTimeOrder: + type: object + required: + - orderType + - customerId + - websiteId + - items + properties: + id: + type: string + description: ID of the order. + readOnly: true + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + orderType: + description: |- + Specifies the type of order. + An order may be a subscription or a one-time purchase. + type: string + x-basic: true + enum: + - one-time-order + customerId: + $ref: '#/components/schemas/CustomerId' + organizationId: + deprecated: true + readOnly: true + allOf: + - $ref: '#/components/schemas/OrganizationId' + status: + description: Status of the one-time order. + type: string + readOnly: true + enum: + - pending + - abandoned + - completed + - canceled + billingStatus: + description: >- + Billing status of the most recent invoice. + + This value may help you to determine if you should change the + service status of the service, + + such as suspending the service. + type: string + readOnly: true + enum: + - draft + - unpaid + - past-due + - abandoned + - paid + - voided + - refunded + - disputed + - partially-refunded + - partially-paid + websiteId: + x-sortable: true + x-basic: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + currency: + description: Currency of the order. + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + initialInvoiceId: + description: ID of the initial invoice (`null` for one-time orders). + readOnly: true + type: + - string + - 'null' + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + recentInvoiceId: + description: |- + ID of the most recently issued invoice. + The invoice might not be `paid` yet. + readOnly: true + type: + - string + - 'null' + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + items: + description: Details of items in the order. + type: array + minItems: 1 + items: + $ref: '#/components/schemas/OrderItem' + deliveryAddress: + description: Delivery address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + billingAddress: + description: Billing address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + activationTime: + description: Date and time when the order is activated. + x-sortable: true + type: + - string + - 'null' + format: date-time + readOnly: true + voidTime: + description: Date and time when the order is voided. + type: + - string + - 'null' + format: date-time + readOnly: true + abandonTime: + type: + - string + - 'null' + description: >- + Date and time when the pending order is automatically abandoned. + + If this value is not passed during order creation, + + a [pending order + TTL](https://all-rebilly.redoc.ly/tag/Organizations/operation/PatchOrganization/#!path=settings/billing/pendingOrderTtl&t=request) + setting is used to calculate the value. + format: date-time + couponIds: + type: + - array + - 'null' + description: >- + List of coupons to redeem on the customer and restrict to this + order. + + + For more information, see + [Coupons](https://www.rebilly.com/docs/settings/coupons-and-discounts/). + + + This parameter uses the following logic: + + + - If this parameter is not supplied, applied coupons are not + changed. + + - If an empty array is supplied, all applied coupon redemptions are + canceled. + + - If a list of coupons is supplied, unapplied coupons in the list + are applied. + Coupons that have already been applied do not change state. + Applied coupons that are not supplied in list are canceled. + + If the list of applied coupons on a pending order is changed by this + parameter during an order update, the invoice for the order is + reissued. + writeOnly: true + items: + type: string + description: ID of the coupon. + poNumber: + description: Purchase order number displayed on the issued invoices. + type: + - string + - 'null' + example: PO123456 + shipping: + $ref: '#/components/schemas/Shipping' + notes: + description: Notes for the customer displayed on the order invoice. + type: string + revision: + description: >- + Number of times the order data has been modified. + + + The revision is useful when analyzing webhook data to determine if + the + + change takes precedence over the current representation. + type: integer + readOnly: true + riskMetadata: + example: null + description: >- + Risk metadata. + + If this value is `null`, this field uses risk metadata that is + captured when creating the payment token. + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - website + - customer + - initialInvoice + - recentInvoice + - approvalUrl + - attachments + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + recentInvoice: + type: object + customer: + type: object + website: + type: object + leadSource: + type: object + shippingRate: + type: object + paymentInstrument: + type: object + Subscription: + type: object + description: Subscription details. + discriminator: + propertyName: orderType + mapping: + subscription-order: '#/components/schemas/SubscriptionOrder' + one-time-order: '#/components/schemas/OneTimeOrder' + anyOf: + - $ref: '#/components/schemas/SubscriptionOrder' + - $ref: '#/components/schemas/OneTimeOrder' + Search: + type: object + properties: + customers: + description: List of returned customers. + readOnly: true + type: array + items: + $ref: '#/components/schemas/Customer' + invoices: + description: List of returned invoices. + readOnly: true + type: array + items: + $ref: '#/components/schemas/Invoice' + orders: + description: List of returned orders. + readOnly: true + type: array + items: + $ref: '#/components/schemas/Subscription' + transactions: + description: List of returned transactions. + readOnly: true + type: array + items: + $ref: '#/components/schemas/Transaction' + searched: + description: |- + Names of searched resources. + Includes all searched resources. + readOnly: true + type: array + items: + type: string + ShippingOption: + type: object + required: + - name + - price + - currency + properties: + id: + type: string + description: ID of the shipping rate. + readOnly: true + maxLength: 50 + example: ship_rate_0YVDN408G4DQE9G1RV1QCFHJ3P + name: + type: string + description: Name of the shipping rate. + description: + type: + - string + - 'null' + description: Description of the shipping rate. + price: + description: |- + Price of the shipping rate. + If `price` is `0`, shipping is free. + type: number + format: double + currency: + $ref: '#/components/schemas/CurrencyCode' + ShippingRate: + allOf: + - $ref: '#/components/schemas/ShippingOption' + - properties: + filter: + type: string + description: >- + Filter based on invoice properties that is used to determine + when the rate is applicable. + + If no filters are used, the rate is always applicable. + default: '' + example: 'deliveryAddress.country:US,CA,RU;amount:100..1000' + status: + description: |- + Status of the shipping rate. + If `status` is `inactive` the shipping rate is not applied. + type: string + enum: + - active + - inactive + default: active + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + SubscriptionChange: + type: object + required: + - items + - renewalPolicy + - prorated + properties: + items: + description: Details of items in the order. + type: array + minItems: 1 + items: + type: object + description: >- + New set of items for the subscription. + + To remove an item, include the items array and exclude the items + you want to remove. + required: + - plan + - quantity + properties: + plan: + description: Plan details. + oneOf: + - $ref: '#/components/schemas/OriginalPlan' + - $ref: '#/components/schemas/FlexiblePlan' + quantity: + description: Number of units of the product on the given plan. + type: integer + usageLimits: + type: object + description: >- + Specifies the soft and hard usage limit thresholds for an item + with a metered billing plan. + + This value is ignored when the plan is not metered. + properties: + softLimit: + type: object + description: >- + Defines thresholds for notification purposes. + + For example, to notify the customer that their usage is + near the hard limit of their metered billing plan. + + This notification informs the customer so that they can + upgrade their plan before the hard limit is reached. + + When the reported usage reaches 75%, 90%, and 100% of the + configured limit a specific event is fired. + + To notify the customer, a webhook and notification can be + configured for this event. + + This field is useful for accounting and customer success + purposes. + + The usage of metered billing plans can still be reported + if the soft limit is reached. + minProperties: 1 + maxProperties: 1 + properties: + quantity: + type: integer + description: Usage limit quantity. + minimum: 1 + amount: + type: number + format: double + description: Usage limit amount in the currency of the order. + minimum: 0.01 + hardLimit: + type: object + description: >- + Defines a limit where the customer can no longer use the + service. + + Hard limits are used in addition to soft limits. + + When a soft limit is reached, + + a customer may receive a notification + + but the service can still be provided up to the hard limit + value so that the customer can upgrade their plan. + + When the reported usage reaches the configured limit, + + a specific event is triggered. + + To notify the customer in the merchant system, + + or block a service, + + a webhook and notification can be configured for this + event. + + When the total usage reaches the hard limit quantity, + + or amount values, + + metered billing plan usages can no longer be reported. + minProperties: 1 + maxProperties: 1 + properties: + quantity: + type: integer + description: Usage limit quantity. + minimum: 1 + amount: + type: number + format: double + description: Usage limit amount in the currency of the order. + minimum: 0.01 + renewalPolicy: + description: >- + Specifies if the subscription retains its current `renewalTime` or + resets it to a newly calculated `renewalTime`. + type: string + enum: + - reset + - retain + prorated: + description: >- + Specifies whether to give a pro rata credit for the amount of time + remaining between the `effectiveTime` and the end of the current + period. + + + In addition, if the `renewalTime` is retained, by setting the + `renewalPolicy` to `retain`, + + a pro rata debit occurs for the amount between the `effectiveTime` + and the `renewalTime` as a percentage of the normal period size. + type: boolean + effectiveTime: + description: >- + Date from which the renewal time for `reset` operations and + proration calculations are made. + + If this field is omitted, this value defaults to the current time. + type: string + format: date-time + preview: + description: |- + Specifies if changes to the subscription can be previewed. + Subscriptions cannot be changed in preview. + type: boolean + default: false + keepTrial: + description: |- + Specifies if the subscription order must retain its active trial. + This field is only applicable if `renewalPolicy` is set to `retain`. + type: boolean + default: false + SubscriptionInvoice: + type: object + properties: + transactionId: + description: >- + If present, applies a payment to the invoice created. If the payment + is for the invoice total, it would be marked as paid. + $ref: '#/components/schemas/TransactionId' + SubscriptionPause: + type: object + description: Subscription pause details. + required: + - subscriptionId + properties: + id: + type: string + description: ID of the subscription pause. + readOnly: true + maxLength: 50 + example: ord_pau_01H085J3ZR1WKD120D73D7N4C9 + subscriptionId: + type: string + description: ID of the paused subscription. + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + status: + description: Status of the subscription pause. + type: string + readOnly: true + enum: + - pending + - ongoing + - revoked + - finished + pausedBy: + description: Specifies who initiated the pause. + type: string + default: customer + enum: + - merchant + - customer + description: + description: Description of the pause reason in free form. + type: + - string + - 'null' + maxLength: 255 + effectiveTime: + description: >- + Date and time when the service period pauses. + + + This time must be later than the current time. + + If this time is earlier then the current time, the current time is + used. + + + If this field is omitted, this value defaults to the current time. + type: + - string + - 'null' + format: date-time + endTime: + description: >- + Date and time when the pause ends and the subscription resumes + billing. + + + To resume a subscription from this point in time, + + use the current time or an earlier time. + + If `endTime` is earlier then the current time, the current time is + used. + + If this field is empty, the subscription is indefinitely paused. + type: + - string + - 'null' + format: date-time + timeRemaining: + description: >- + Amount of time between the pause end time and the renewal time in + ISO-8601 durations format. + + By default, this value is computed as the time between the pause and + the renewal. + + + Example: A $30 per month subscription on a 30-day month is paused on + day 20 of the subscription. + + There are 10 unused days on the subscription. + + Therefore, the subscription renews in 10 days after it is resumed. + + The upcoming invoice does not change the amount for the invoiced + period, it remains at $30. + + If the subscription uses a billing anchor, + + the renewal time after a pause shift behaves as though the renewal + time is moved forward by an [Order API operation](../../tag/Orders), + + and the billing anchor is ignored for that period. + + + For more information, see [Service period anchor, billing timing, + and invoice time + shift](https://www.rebilly.com/docs/dev-docs/concepts/#service-period-anchor-and-billing-timing-and-invoice-time-shift). + type: + - string + - 'null' + example: P3600S + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + SubscriptionCancellation: + type: object + required: + - subscriptionId + - churnTime + properties: + id: + type: string + description: ID of the cancellation. + readOnly: true + maxLength: 50 + example: ord_cnl_0YVJ5XVQM9CDP8248ZQX0RDMKV + subscriptionId: + type: string + description: ID of the canceled subscription order. + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + proratedInvoiceId: + type: + - string + - 'null' + description: ID of the invoice on which the cancellation proration is calculated. + readOnly: true + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + appliedInvoiceId: + type: + - string + - 'null' + description: >- + ID of the invoice on which the cancellation fees or credits are + applied. + readOnly: true + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + canceledBy: + description: Specifies who initiated the cancellation. + type: string + default: customer + enum: + - merchant + - customer + - rebilly + reason: + description: Reason for the cancellation. + type: string + default: other + enum: + - did-not-use + - did-not-want + - missing-features + - bugs-or-problems + - do-not-remember + - risk-warning + - contract-expired + - too-expensive + - other + - billing-failure + description: + description: Description of the cancellation reason in free form. + type: string + maxLength: 255 + prorated: + description: >- + Specifies if the customer gets a pro-rata credit for the time + remaining between `churnTime` and subscription next renewal time. + type: boolean + default: false + status: + description: Status of the subscription order. + type: string + default: confirmed + enum: + - draft + - confirmed + - completed + - revoked + x-enumDescriptions: + draft: >- + Creates a daft cancellation so that the cancellation and charge + can be previewed. + confirmed: |- + Confirms a subscription cancellation. + Sets the subscription to cancel when the `churnTime` is reached. + completed: >- + Marks a subscription cancellation as completed. + + This is a read-only status that is set when the `churnTime` is + reached. + + The cancellation may not be changed or deleted when the status is + `completed`. + revoked: Revokes a subscription cancellation. + canceledTime: + description: >- + Date and time when a subscription is cancelled. + + By default, this occurs when `status` is `confirmed`, unless `draft` + is specified. + type: + - string + - 'null' + format: date-time + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + churnTime: + description: Date and time when the subscription is deactivated. + type: string + format: date-time + lineItems: + description: |- + Items to be added to the new invoice. + Proration item is generated and added automatically. + type: array + items: + type: object + description: Invoice line item. + required: + - type + - unitPriceAmount + - unitPriceCurrency + - quantity + properties: + type: + description: Type of invoice line item. + type: string + enum: + - debit + - credit + description: + description: Description of the line item. + type: string + maxLength: 1000 + unitPriceAmount: + description: Unit price of the line item. + type: number + format: double + example: 49.95 + unitPriceCurrency: + $ref: '#/components/schemas/CurrencyCode' + quantity: + description: Quantity of the line item. + type: integer + example: 1 + periodStartTime: + description: Date and time when the period begins for this item. + type: string + format: date-time + periodEndTime: + description: Date and time when the period ends for this item. + type: string + format: date-time + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + lineItemSubtotal: + description: >- + Subtotal of the line items added after the subscription + cancellation. + readOnly: true + type: object + properties: + amount: + type: number + description: Subtotal amount of the line items. + example: 49.95 + currency: + $ref: '#/components/schemas/CurrencyCode' + _links: + $ref: '#/components/schemas/SelfLink' + SubscriptionReactivation: + type: object + required: + - subscriptionId + properties: + id: + type: string + description: ID of the reactivation. + readOnly: true + maxLength: 50 + example: ord_rct_0YVJ62AF5XCFCA6EBFAAA3Z7E0 + subscriptionId: + type: string + description: ID of the reactivated subscription. + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + cancellationId: + type: string + description: ID of the related cancellation. + readOnly: true + maxLength: 50 + example: ord_cnl_0YVJ5XVQM9CDP8248ZQX0RDMKV + description: + description: Description of the reactivation reason in free form. + type: + - string + - 'null' + maxLength: 255 + effectiveTime: + description: |- + Date and time when the service period starts, + unless the subscription is canceled but still active. + + If the subscription is still active, + the subscription continues for the current service period. + + If this field is omitted, this value defaults to the current time. + type: string + format: date-time + writeOnly: true + renewalTime: + description: >- + Date and time of the next subscription renewal. + + If this field is omitted, this value is computed from the + `effectiveTime` field. + + + If the subscription is canceled but still active, + + it is ignored and the next renewal occurs as scheduled. + type: string + format: date-time + paymentInstrumentId: + writeOnly: true + description: >- + ID of the payment instrument. + + If this field is omitted, the subscription payment instrument + remains unchanged. + type: + - string + - 'null' + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + UpcomingInvoiceItem: + type: object + readOnly: true + properties: + id: + type: string + description: ID of the upcoming invoice item. + maxLength: 50 + example: ii_0YVFDEQS2KCFTBN9HXWJFY55GV + isInterim: + description: Specifies if the line item is added to an interim invoice. + type: boolean + description: + description: Description of the upcoming invoice item. + type: string + maxLength: 1000 + unitPrice: + description: Unit price of the upcoming invoice item. + type: number + format: double + quantity: + description: Quantity of the upcoming invoice item. + type: integer + price: + description: Total price of the upcoming invoice item. + type: number + format: double + productId: + type: + - string + - 'null' + description: ID of the product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + planId: + type: + - string + - 'null' + description: ID of the plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + subscriptionId: + type: string + description: ID of the order. + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + discountAmount: + description: Discount amount applied to the upcoming invoice item. + type: number + format: double + periodStartTime: + description: Date and time when the billing period starts. + type: string + format: date-time + periodEndTime: + description: Date and time when the billing period ends. + type: string + format: date-time + periodNumber: + description: >- + Billing period number that is associated with the invoice item. + + For example, an invoice item for a service is included in billing + period number 3. + + The invoice item is only applied to billing period number 3. + type: integer + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + tax: + description: Invoice item tax. + $ref: '#/components/schemas/TaxItem' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - product + - plan + - subscription + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + properties: + product: + type: object + plan: + type: object + UpcomingInvoice: + type: object + readOnly: true + properties: + id: + description: >- + ID of the upcoming invoice, which is persisted in the future renewal + invoice. + type: string + readOnly: true + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + websiteId: + $ref: '#/components/schemas/WebsiteId' + subscriptionId: + description: ID of the related subscription order. + type: string + readOnly: true + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + currency: + x-sortable: true + x-basic: true + $ref: '#/components/schemas/CurrencyCode' + customerId: + x-basic: true + allOf: + - $ref: '#/components/schemas/CustomerId' + revision: + description: |- + Number of times the upcoming invoice data has changed. + + Use the revision number when analyzing webhook data to + determine if a change should take precedence over the current + representation. + type: integer + organizationId: + allOf: + - $ref: '#/components/schemas/OrganizationId' + items: + type: array + description: Upcoming invoice items array. + items: + $ref: '#/components/schemas/UpcomingInvoiceItem' + amount: + description: Amount of the invoice. + type: number + x-type: Money + x-sortable: true + x-basic: true + format: double + readOnly: true + amountDue: + description: Amount that is due on the invoice. + type: number + x-type: Money + x-sortable: true + format: double + readOnly: true + subtotalAmount: + description: Subtotal amount of the invoice. + type: number + x-type: Money + format: double + readOnly: true + discountAmount: + description: Discount amount that is applied to the invoice. + type: number + x-type: Money + format: double + readOnly: true + shipping: + $ref: '#/components/schemas/Shipping' + tax: + $ref: '#/components/schemas/Taxes' + organizationTaxIdNumber: + description: Organization tax ID number that is displayed on the invoice. + type: + - object + - 'null' + required: + - type + - value + properties: + type: + type: string + description: Type of the tax ID number. + enum: + - eu-vat + - other + example: eu-vat + value: + type: string + description: Value of the tax ID number. + example: GB980780684 + customerTaxIdNumber: + description: Customer tax ID number that is displayed on the invoice. + type: + - object + - 'null' + required: + - type + - value + properties: + type: + type: string + description: Type of the tax ID number. + enum: + - eu-vat + - other + example: eu-vat + value: + type: string + description: Value of the tax ID number. + example: GB980780684 + billingAddress: + description: Billing address of the invoice. + $ref: '#/components/schemas/ContactObject' + deliveryAddress: + description: Delivery address of the invoice. + $ref: '#/components/schemas/ContactObject' + poNumber: + description: Purchase order number that is displayed on the invoice. + type: + - string + - 'null' + example: PO123456 + notes: + description: Notes for the customer that are displayed on the invoice. + type: string + discounts: + type: array + description: Discounts applied. + readOnly: true + items: + type: object + readOnly: true + properties: + couponId: + type: string + description: ID of the coupon. + maxLength: 50 + example: cpn_0YVCNKF81GD778N4YNVGDJK558 + redemptionId: + description: ID of the redemption. + $ref: '#/components/schemas/ResourceId' + amount: + description: Total amount discounted by this coupon. + type: number + format: double + description: + type: string + description: Description of the discount. + context: + $ref: '#/components/schemas/DiscountContext' + dueTime: + description: Date and time when the invoice is due for payment. + type: string + x-sortable: true + format: date-time + issuedTime: + description: Date and time when the invoice is issued. + x-label: Date Issued + x-sortable: true + x-basic: true + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - website + - customer + - organization + - subscription + - attachments + - transactionAllocations + - recalculateInvoice + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + properties: + customer: + type: object + website: + type: object + organization: + type: object + leadSource: + type: object + OrderTimeline: + type: object + properties: + id: + type: string + description: ID of the timeline message. + readOnly: true + maxLength: 50 + example: tmln_0YV8Q9WEF5DTA8HFXS27D1G6GC + type: + description: Type of timeline message. + type: string + readOnly: true + enum: + - coupon-applied + - email-message-sent + - invoice-abandoned + - invoice-disputed + - invoice-issued + - invoice-paid + - invoice-partially-paid + - invoice-partially-refunded + - invoice-past-due + - invoice-refunded + - invoice-reissued + - invoice-renewal-payment-declined + - invoice-voided + - order-activated + - order-autopay-changed + - order-billing-address-changed + - order-billing-anchor-changed + - order-canceled + - order-churned + - order-completed + - order-custom-fields-changed + - order-delivery-address-changed + - order-downgraded + - order-items-changed + - order-paid-early + - order-quantity-changed + - order-reactivated + - order-recurring-interval-changed + - order-renewal-time-changed + - order-renewed + - order-risk-metadata-changed + - order-upgraded + - order-voided + - order-abandoned + - subscription-paused + - subscription-pause-created + - subscription-pause-modified + - subscription-pause-revoked + - subscription-resumed + - subscription-trial-end-changed + - timeline-comment-created + triggeredBy: + description: 'Specifies who, or what, triggered the timeline event.' + type: string + readOnly: true + enum: + - rebilly + - app + - direct-api + message: + description: Contents of the timeline message. + type: string + extraData: + $ref: '#/components/schemas/TimelineExtraData' + occurredTime: + description: Date and time when the timeline message occurred. + readOnly: true + $ref: '#/components/schemas/ServerTimestamp' + _links: + $ref: '#/components/schemas/SelfLink' + Usage: + type: object + required: + - subscriptionId + - planId + - quantity + properties: + id: + type: string + readOnly: true + description: ID of the usage record. + maxLength: 50 + example: ord_usg_0YVJ636B95DNA9M3B1638HXBCQ + subscriptionId: + type: string + description: ID of the subscription for which a usage is reported. + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + planId: + type: string + description: ID of the plan for which a usage is reported. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + invoiceId: + type: + - string + - 'null' + description: >- + ID of the invoice to which usage is applied. This value is populated + when the invoice is issued. + readOnly: true + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + invoiceItemId: + description: >- + ID of the invoice item to which usage is applied. This value is + populated when the invoice is issued. + readOnly: true + type: + - string + - 'null' + maxLength: 50 + example: ii_0YVFDEQS2KCFTBN9HXWJFY55GV + quantity: + description: |- + Number of used product units of a plan. + Products are the goods and services that your business sells. + Plans describe how the customer must pay for products. + type: number + format: float + minimum: 0.01 + usageTime: + description: >- + Date and time, in ISO 8601 format, when a usage occurred. + + If this value is not provided or is empty, the date and time of the + request is used. + type: string + format: date-time + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + PaymentCardToken: + type: object + title: Payment card token + description: >- + Creates a payment card token. + + A payment card token is a string that represents a customer's payment + card details. + + It can be used once, and expires upon first use or within 30 minutes of + token creation. + + To generate a full payment card token, you must provide the + `billingAddress`. + + If `billingAddress` is not provided, the token can not be used for + payments. + required: + - method + - paymentInstrument + properties: + method: + description: Payment method of the token. + type: string + enum: + - payment-card + paymentInstrument: + description: Payment card instrument details. + type: object + required: + - expMonth + - expYear + properties: + pan: + description: |- + Primary Account Number (PAN) of the payment card. + This value is required to perform a payment. + type: string + writeOnly: true + cvv: + description: Card Verification Value (CVV/CVC) of the payment card. + type: string + writeOnly: true + expMonth: + description: Expiration month of the payment card. + type: integer + expYear: + description: Expiration year of the payment card. + type: integer + bin: + description: |- + Bank Identification Number (BIN) of the payment card. + This value is the first 6 digits of the payment card number. + type: + - string + - 'null' + format: bin + readOnly: true + last4: + description: >- + Last 4 digits of the Primary Account Number (PAN) of the payment + card. + type: + - string + - 'null' + readOnly: true + brand: + readOnly: true + allOf: + - $ref: '#/components/schemas/PaymentCardBrand' + billingAddress: + description: |- + Billing address object. + This value is required to perform payments. + For payment-card updates, `billingAddress` can be ignored. + $ref: '#/components/schemas/ContactObject' + id: + description: ID of the token. + readOnly: true + $ref: '#/components/schemas/ResourceId' + isUsed: + description: Specifies if the token has been used. + type: boolean + default: false + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + leadSource: + allOf: + - $ref: '#/components/schemas/LeadSource' + writeOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + usageTime: + description: Date and time when the token is used. + type: + - string + - 'null' + format: date-time + readOnly: true + expirationTime: + description: Date and time when the token expired. + type: + - string + - 'null' + format: date-time + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + PayPalToken: + type: object + title: PayPal token + required: + - method + - paymentInstrument + properties: + method: + description: Payment method of the token. + type: string + enum: + - paypal + paymentInstrument: + description: Paypal instrument details required for express checkout. + type: object + required: + - billingAgreementToken + - paypalMerchantId + properties: + paypalMerchantId: + description: ID of the PayPal merchant. + type: string + billingAgreementToken: + description: PayPal billing agreement token. + type: string + billingAddress: + description: Billing address object. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + id: + description: ID of the token. + readOnly: true + $ref: '#/components/schemas/ResourceId' + isUsed: + description: Specifies if the token is already used. + type: boolean + default: false + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + leadSource: + allOf: + - $ref: '#/components/schemas/LeadSource' + writeOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + usageTime: + description: Date and time when the token is used. + type: + - string + - 'null' + format: date-time + readOnly: true + expirationTime: + description: Date and time when the token expires. + type: + - string + - 'null' + format: date-time + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + BBANInstrument: + description: Bank account BBAN instrument. + type: object + required: + - accountNumberType + - accountNumber + - routingNumber + - accountType + properties: + accountNumberType: + description: >- + Bank account number type. + + A valid value is basic bank account number (BBAN) or international + bank account number (IBAN). + type: string + enum: + - BBAN + accountNumber: + description: Customer's bank account number. + type: string + pattern: '^[0-9]+$' + writeOnly: true + routingNumber: + description: Bank routing number. + type: string + pattern: '^[0-9]+$' + accountType: + description: Bank account type. + type: string + enum: + - checking + - savings + - other + bic: + description: Bank Identifier Code (BIC). + type: string + bankName: + description: Bank name. + type: string + last4: + description: Last 4 digits of the bank account number. + type: string + readOnly: true + IBANInstrument: + description: Bank account IBAN instrument. + type: object + required: + - accountNumberType + - accountNumber + properties: + accountNumberType: + description: >- + Bank account number type. + + A valid value is basic bank account number (BBAN) or international + bank account number (IBAN). + type: string + enum: + - IBAN + accountNumber: + description: >- + Bank account number. + + Detailed information about all ISO 13616-compliant national IBAN + formats is available in the [SWIFT IBAN + Registry](https://www.swift.com/standards/data-standards/iban). + type: string + writeOnly: true + bic: + description: Bank Identifier Code (BIC). + type: string + bankName: + description: Bank name. + type: string + last4: + description: Last 4 digits of the bank account number. + type: string + readOnly: true + BankAccountInstrument: + type: object + description: Bank account BBAN or IBAN instrument. + discriminator: + propertyName: accountNumberType + mapping: + BBAN: '#/components/schemas/BBANInstrument' + IBAN: '#/components/schemas/IBANInstrument' + anyOf: + - $ref: '#/components/schemas/BBANInstrument' + - $ref: '#/components/schemas/IBANInstrument' + BankAccountToken: + type: object + title: Bank account token + required: + - method + - paymentInstrument + - billingAddress + properties: + method: + description: Payment method of the token. + type: string + enum: + - ach + - echeck + paymentInstrument: + $ref: '#/components/schemas/BankAccountInstrument' + billingAddress: + description: Billing address object. + $ref: '#/components/schemas/ContactObject' + id: + description: ID of the token. + readOnly: true + $ref: '#/components/schemas/ResourceId' + isUsed: + description: Specifies if the token is already used. + type: boolean + default: false + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + leadSource: + allOf: + - $ref: '#/components/schemas/LeadSource' + writeOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + usageTime: + description: Date and time when the token is used. + type: + - string + - 'null' + format: date-time + readOnly: true + expirationTime: + description: Date and time when the token expired. + type: + - string + - 'null' + format: date-time + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + DigitalWalletToken: + type: object + title: Digital wallet token + required: + - method + - paymentInstrument + properties: + method: + description: Payment method of the token. + type: string + enum: + - digital-wallet + paymentInstrument: + description: Payment instrument details. + type: object + required: + - type + - amount + - currency + - descriptor + - payload + properties: + type: + description: Type of digital wallet. + type: string + enum: + - Apple Pay + - Google Pay + amount: + description: Authorized for the digital wallet amount. + type: number + format: double + currency: + description: Authorized for the digital wallet currency. + $ref: '#/components/schemas/CurrencyCode' + descriptor: + description: Descriptor for a payment. + type: string + bin: + description: >- + Bank Identification Number (BIN) of the payment card. + + This value is the same as the first 6 digits of the associated + Primary Account Number (PAN). + type: + - string + - 'null' + format: bin + readOnly: true + last4: + description: >- + Last 4 digits of the Primary Account Number (PAN) of the payment + card. + type: string + readOnly: true + brand: + readOnly: true + allOf: + - $ref: '#/components/schemas/PaymentCardBrand' + expMonth: + readOnly: true + description: Payment card expiration month. + type: integer + expYear: + readOnly: true + description: Payment card expiration year. + type: integer + payload: + writeOnly: true + description: |- + Digital wallet encoded data. + This field may contain the digital wallet billing address. + type: object + billingAddress: + readOnly: true + description: Billing address object. + $ref: '#/components/schemas/ContactObject' + id: + description: ID of the token. + readOnly: true + $ref: '#/components/schemas/ResourceId' + isUsed: + description: Specifies if the token is already used. + type: boolean + default: false + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + leadSource: + allOf: + - $ref: '#/components/schemas/LeadSource' + writeOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + usageTime: + description: Date and time when the token is used. + type: + - string + - 'null' + format: date-time + readOnly: true + expirationTime: + description: Date and time when the token expired. + type: + - string + - 'null' + format: date-time + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + PlaidAccountToken: + type: object + title: Plaid account token + required: + - method + - billingAddress + - paymentInstrument + properties: + method: + description: Payment method of the token. + type: string + enum: + - plaid-account + paymentInstrument: + description: Plaid payment instrument details. + type: object + required: + - linkToken + - publicToken + - accountId + properties: + linkToken: + type: string + description: Plaid link token. + writeOnly: true + publicToken: + type: string + description: Plaid public token. + accountId: + type: string + description: ID of the Plaid account. + billingAddress: + description: Billing address object. + $ref: '#/components/schemas/ContactObject' + id: + description: ID of the token. + readOnly: true + $ref: '#/components/schemas/ResourceId' + isUsed: + description: Specifies if the token is already used. + type: boolean + default: false + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + leadSource: + allOf: + - $ref: '#/components/schemas/LeadSource' + writeOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + usageTime: + description: Date and time when the token is used. + type: + - string + - 'null' + format: date-time + readOnly: true + expirationTime: + description: Date and time when the token expired. + type: + - string + - 'null' + format: date-time + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + KhelocardCardToken: + type: object + title: Khelocard card token + required: + - method + - billingAddress + - paymentInstrument + properties: + method: + description: Payment method of the token. + type: string + enum: + - Khelocard + paymentInstrument: + description: Token instrument details. + type: object + required: + - number + - cvv + - expYear + - expMonth + properties: + number: + description: Khelocard card number. + type: string + writeOnly: true + cvv: + description: Khelocard Card Verification Value (CVV). + type: string + writeOnly: true + last4: + description: Last 4 digits of the Khelocard card number. + type: string + readOnly: true + expMonth: + description: Khelocard card expiration month. + type: integer + expYear: + description: Khelocard card expiration year. + type: integer + billingAddress: + description: Billing address object. + $ref: '#/components/schemas/ContactObject' + id: + description: ID of the token. + readOnly: true + $ref: '#/components/schemas/ResourceId' + isUsed: + description: Specifies if the token is already used. + type: boolean + default: false + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + leadSource: + allOf: + - $ref: '#/components/schemas/LeadSource' + writeOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + usageTime: + description: Date and time when the token is used. + type: + - string + - 'null' + format: date-time + readOnly: true + expirationTime: + description: Date and time when the token expired. + type: + - string + - 'null' + format: date-time + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + KlarnaToken: + type: object + title: Klarna token + required: + - method + - paymentInstrument + properties: + method: + description: Payment method of the token. + type: string + enum: + - Klarna + paymentInstrument: + description: Klarna instrument details required for express checkout. + type: object + required: + - klarnaAuthorizationToken + - klarnaSessionId + properties: + klarnaAuthorizationToken: + description: Klarna authorization token. + type: string + klarnaSessionId: + description: ID of the Klarna session. + type: string + billingAddress: + description: Billing address object. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + id: + description: ID of the token. + readOnly: true + $ref: '#/components/schemas/ResourceId' + isUsed: + description: Specifies if the token is already used. + type: boolean + default: false + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + leadSource: + allOf: + - $ref: '#/components/schemas/LeadSource' + writeOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + usageTime: + description: Date and time when the token is used. + type: + - string + - 'null' + format: date-time + readOnly: true + expirationTime: + description: Date and time when the token expired. + type: + - string + - 'null' + format: date-time + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + AlternativePaymentToken: + type: object + title: Alternative payment token + required: + - method + - billingAddress + properties: + method: + description: Payment method of the token. + type: string + allOf: + - $ref: '#/components/schemas/AlternativePaymentMethods' + - not: + enum: + - payment-card + - paypal + - ach + - echeck + - digital-wallet + - plaid-account + - Khelocard + - Klarna + billingAddress: + description: Billing address object. + $ref: '#/components/schemas/ContactObject' + id: + description: ID of the token. + readOnly: true + $ref: '#/components/schemas/ResourceId' + isUsed: + description: Specifies if the token is already used. + type: boolean + default: false + readOnly: true + riskMetadata: + oneOf: + - $ref: '#/components/schemas/RiskMetadata' + - type: 'null' + leadSource: + allOf: + - $ref: '#/components/schemas/LeadSource' + writeOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + usageTime: + description: Date and time when the token is used. + type: + - string + - 'null' + format: date-time + readOnly: true + expirationTime: + description: Date and time when the token expired. + type: + - string + - 'null' + format: date-time + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + CompositeToken: + type: object + discriminator: + propertyName: method + mapping: + payment-card: '#/components/schemas/PaymentCardToken' + paypal: '#/components/schemas/PayPalToken' + ach: '#/components/schemas/BankAccountToken' + echeck: '#/components/schemas/BankAccountToken' + digital-wallet: '#/components/schemas/DigitalWalletToken' + plaid-account: '#/components/schemas/PlaidAccountToken' + Khelocard: '#/components/schemas/KhelocardCardToken' + Klarna: '#/components/schemas/KlarnaToken' + cash: '#/components/schemas/AlternativePaymentToken' + check: '#/components/schemas/AlternativePaymentToken' + AdvCash: '#/components/schemas/AlternativePaymentToken' + Alfa-click: '#/components/schemas/AlternativePaymentToken' + Alipay: '#/components/schemas/AlternativePaymentToken' + AstroPay Card: '#/components/schemas/AlternativePaymentToken' + AstroPay-GO: '#/components/schemas/AlternativePaymentToken' + BankReferenced: '#/components/schemas/AlternativePaymentToken' + bank-transfer: '#/components/schemas/AlternativePaymentToken' + bank-transfer-2: '#/components/schemas/AlternativePaymentToken' + bank-transfer-3: '#/components/schemas/AlternativePaymentToken' + bank-transfer-4: '#/components/schemas/AlternativePaymentToken' + bank-transfer-5: '#/components/schemas/AlternativePaymentToken' + bank-transfer-6: '#/components/schemas/AlternativePaymentToken' + bank-transfer-7: '#/components/schemas/AlternativePaymentToken' + bank-transfer-8: '#/components/schemas/AlternativePaymentToken' + bank-transfer-9: '#/components/schemas/AlternativePaymentToken' + Baloto: '#/components/schemas/AlternativePaymentToken' + Beeline: '#/components/schemas/AlternativePaymentToken' + Belfius-direct-net: '#/components/schemas/AlternativePaymentToken' + bitcoin: '#/components/schemas/AlternativePaymentToken' + Bizum: '#/components/schemas/AlternativePaymentToken' + Boleto: '#/components/schemas/AlternativePaymentToken' + cash-deposit: '#/components/schemas/AlternativePaymentToken' + CASHlib: '#/components/schemas/AlternativePaymentToken' + CashToCode: '#/components/schemas/AlternativePaymentToken' + China UnionPay: '#/components/schemas/AlternativePaymentToken' + Cleo: '#/components/schemas/AlternativePaymentToken' + CODVoucher: '#/components/schemas/AlternativePaymentToken' + Conekta-oxxo: '#/components/schemas/AlternativePaymentToken' + Cupon-de-pagos: '#/components/schemas/AlternativePaymentToken' + cryptocurrency: '#/components/schemas/AlternativePaymentToken' + domestic-cards: '#/components/schemas/AlternativePaymentToken' + ecoPayz: '#/components/schemas/AlternativePaymentToken' + ecoVoucher: '#/components/schemas/AlternativePaymentToken' + Efecty: '#/components/schemas/AlternativePaymentToken' + EPS: '#/components/schemas/AlternativePaymentToken' + ePay.bg: '#/components/schemas/AlternativePaymentToken' + eZeeWallet: '#/components/schemas/AlternativePaymentToken' + FasterPay: '#/components/schemas/AlternativePaymentToken' + Flexepin: '#/components/schemas/AlternativePaymentToken' + Giropay: '#/components/schemas/AlternativePaymentToken' + Gpaysafe: '#/components/schemas/AlternativePaymentToken' + Google Pay: '#/components/schemas/AlternativePaymentToken' + iDebit: '#/components/schemas/AlternativePaymentToken' + iDEAL: '#/components/schemas/AlternativePaymentToken' + ING-homepay: '#/components/schemas/AlternativePaymentToken' + INOVAPAY-pin: '#/components/schemas/AlternativePaymentToken' + INOVAPAY-wallet: '#/components/schemas/AlternativePaymentToken' + InstaDebit: '#/components/schemas/AlternativePaymentToken' + instant-bank-transfer: '#/components/schemas/AlternativePaymentToken' + InstantPayments: '#/components/schemas/AlternativePaymentToken' + Interac: '#/components/schemas/AlternativePaymentToken' + Interac-online: '#/components/schemas/AlternativePaymentToken' + Interac-eTransfer: '#/components/schemas/AlternativePaymentToken' + invoice: '#/components/schemas/AlternativePaymentToken' + iWallet: '#/components/schemas/AlternativePaymentToken' + Jeton: '#/components/schemas/AlternativePaymentToken' + jpay: '#/components/schemas/AlternativePaymentToken' + KNOT: '#/components/schemas/AlternativePaymentToken' + loonie: '#/components/schemas/AlternativePaymentToken' + Matrix: '#/components/schemas/AlternativePaymentToken' + MaxiCash: '#/components/schemas/AlternativePaymentToken' + Megafon: '#/components/schemas/AlternativePaymentToken' + MiFinity-eWallet: '#/components/schemas/AlternativePaymentToken' + miscellaneous: '#/components/schemas/AlternativePaymentToken' + Bancontact: '#/components/schemas/AlternativePaymentToken' + Bancontact-mobile: '#/components/schemas/AlternativePaymentToken' + MTS: '#/components/schemas/AlternativePaymentToken' + MuchBetter: '#/components/schemas/AlternativePaymentToken' + Multibanco: '#/components/schemas/AlternativePaymentToken' + Neosurf: '#/components/schemas/AlternativePaymentToken' + Netbanking: '#/components/schemas/AlternativePaymentToken' + Neteller: '#/components/schemas/AlternativePaymentToken' + Nordea-Solo: '#/components/schemas/AlternativePaymentToken' + OchaPay: '#/components/schemas/AlternativePaymentToken' + online-bank-transfer: '#/components/schemas/AlternativePaymentToken' + Onlineueberweisen: '#/components/schemas/AlternativePaymentToken' + oriental-wallet: '#/components/schemas/AlternativePaymentToken' + OXXO: '#/components/schemas/AlternativePaymentToken' + P24: '#/components/schemas/AlternativePaymentToken' + Pagadito: '#/components/schemas/AlternativePaymentToken' + PagoEffectivo: '#/components/schemas/AlternativePaymentToken' + Pagsmile-deposit-express: '#/components/schemas/AlternativePaymentToken' + Pagsmile-lottery: '#/components/schemas/AlternativePaymentToken' + PayCash: '#/components/schemas/AlternativePaymentToken' + Payeer: '#/components/schemas/AlternativePaymentToken' + PaymentAsia-crypto: '#/components/schemas/AlternativePaymentToken' + Paymero: '#/components/schemas/AlternativePaymentToken' + Perfect-money: '#/components/schemas/AlternativePaymentToken' + Piastrix: '#/components/schemas/AlternativePaymentToken' + PayTabs: '#/components/schemas/AlternativePaymentToken' + Paysafecard: '#/components/schemas/AlternativePaymentToken' + Paysafecash: '#/components/schemas/AlternativePaymentToken' + Pay4Fun: '#/components/schemas/AlternativePaymentToken' + Paynote: '#/components/schemas/AlternativePaymentToken' + PinPay: '#/components/schemas/AlternativePaymentToken' + phone: '#/components/schemas/AlternativePaymentToken' + PhonePe: '#/components/schemas/AlternativePaymentToken' + POLi: '#/components/schemas/AlternativePaymentToken' + PostFinance-card: '#/components/schemas/AlternativePaymentToken' + PostFinance-e-finance: '#/components/schemas/AlternativePaymentToken' + QIWI: '#/components/schemas/AlternativePaymentToken' + QPay: '#/components/schemas/AlternativePaymentToken' + QQPay: '#/components/schemas/AlternativePaymentToken' + rapyd-checkout: '#/components/schemas/AlternativePaymentToken' + Resurs: '#/components/schemas/AlternativePaymentToken' + SafetyPay: '#/components/schemas/AlternativePaymentToken' + SEPA: '#/components/schemas/AlternativePaymentToken' + Skrill: '#/components/schemas/AlternativePaymentToken' + Skrill Rapid Transfer: '#/components/schemas/AlternativePaymentToken' + SMSVoucher: '#/components/schemas/AlternativePaymentToken' + Sofort: '#/components/schemas/AlternativePaymentToken' + SparkPay: '#/components/schemas/AlternativePaymentToken' + swift-dbt: '#/components/schemas/AlternativePaymentToken' + Tele2: '#/components/schemas/AlternativePaymentToken' + Terminaly-RF: '#/components/schemas/AlternativePaymentToken' + ToditoCash-card: '#/components/schemas/AlternativePaymentToken' + Trustly: '#/components/schemas/AlternativePaymentToken' + UPayCard: '#/components/schemas/AlternativePaymentToken' + UPI: '#/components/schemas/AlternativePaymentToken' + USD-coin: '#/components/schemas/AlternativePaymentToken' + VCreditos: '#/components/schemas/AlternativePaymentToken' + VenusPoint: '#/components/schemas/AlternativePaymentToken' + voucher: '#/components/schemas/AlternativePaymentToken' + voucher-2: '#/components/schemas/AlternativePaymentToken' + voucher-3: '#/components/schemas/AlternativePaymentToken' + voucher-4: '#/components/schemas/AlternativePaymentToken' + Webmoney: '#/components/schemas/AlternativePaymentToken' + Webpay: '#/components/schemas/AlternativePaymentToken' + Webpay-2: '#/components/schemas/AlternativePaymentToken' + Webpay Card: '#/components/schemas/AlternativePaymentToken' + WeChat Pay: '#/components/schemas/AlternativePaymentToken' + XPay-P2P: '#/components/schemas/AlternativePaymentToken' + XPay-QR: '#/components/schemas/AlternativePaymentToken' + Yandex-money: '#/components/schemas/AlternativePaymentToken' + Zotapay: '#/components/schemas/AlternativePaymentToken' + Zimpler: '#/components/schemas/AlternativePaymentToken' + anyOf: + - $ref: '#/components/schemas/PaymentCardToken' + - $ref: '#/components/schemas/PayPalToken' + - $ref: '#/components/schemas/BankAccountToken' + - $ref: '#/components/schemas/DigitalWalletToken' + - $ref: '#/components/schemas/PlaidAccountToken' + - $ref: '#/components/schemas/KhelocardCardToken' + - $ref: '#/components/schemas/KlarnaToken' + - $ref: '#/components/schemas/AlternativePaymentToken' + DigitalWalletValidation: + type: object + required: + - type + discriminator: + propertyName: type + mapping: + Apple Pay: '#/components/schemas/ApplePayValidation' + properties: + type: + description: Type of digital wallet to validate. + type: string + enum: + - Apple Pay + ApplePayValidation: + description: Apple Pay session validation. + allOf: + - $ref: '#/components/schemas/DigitalWalletValidation' + - type: object + required: + - validationRequest + properties: + type: + type: string + validationRequest: + description: Validation request. + type: object + properties: + validationUrl: + type: string + description: Apple Pay SDK URL that is used to perform validation. + domainName: + type: string + description: >- + Domain where the client code, such as + [FramePay](https://docs.rebilly.com/docs/developer-docs/framepay/), + is executed. + + The domain name must be registered in the Apple Pay console. + + For more information, see [Register a domain for Apple + Pay](./PostDigitalWalletOnboardingApplePay). + example: www.example.com + displayName: + type: string + description: Display name of your store. + example: My Store + writeOnly: true + validationResponse: + description: Apple Pay SDK validation response. + type: object + readOnly: true + PaymentInstructionToken: + type: object + title: Payment token + required: + - token + properties: + token: + description: Token ID of the payment. + type: string + PaymentInstructionInstrument: + type: object + title: Payment instrument + required: + - paymentInstrumentId + properties: + paymentInstrumentId: + type: string + description: ID of the payment instrument. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + PaymentInstructionMethods: + type: object + title: Payment Methods + properties: + methods: + type: array + description: >- + List of available payment methods. + + Only payment methods with at least one active gateway account are + allowed. + + + If not specified all available payment methods are displayed. + + + Customer can choose any of those as well as the payment instrument + for them. + + Additional steps occur via a redirect to `approvalUrl`. + items: + $ref: '#/components/schemas/PaymentMethod' + receivedBy: + description: Cash receiver's name. Available only for `cash` payment method. + type: string + reference: + description: Check reference data. Available only for `check` payment method. + type: string + PaymentInstruction: + oneOf: + - $ref: '#/components/schemas/PaymentInstructionToken' + - $ref: '#/components/schemas/PaymentInstructionInstrument' + - $ref: '#/components/schemas/PaymentInstructionMethods' + - $ref: '#/components/schemas/PaymentCardCreatePlain' + - $ref: '#/components/schemas/BankAccountCreatePlain' + TransactionQuery: + type: object + properties: + transactionId: + $ref: '#/components/schemas/TransactionId' + result: + description: Result of the transaction. + type: string + readOnly: true + enum: + - abandoned + - approved + - canceled + - declined + - unknown + status: + description: Status of the transaction. + type: string + readOnly: true + enum: + - completed + - conn-error + - disputed + - never-sent + - offsite + - partially-refunded + - pending + - refunded + - sending + - suspended + - timeout + - voided + - waiting-approval + - waiting-capture + - waiting-gateway + - waiting-refund + amount: + description: Amount of the transaction. + type: number + format: double + readOnly: true + currency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + TransactionUpdate: + type: object + description: >- + Updates the status of a transaction to `completed` with a specified + `result` and optional currency and amount values. + required: + - result + properties: + result: + description: Result of the transaction. + type: string + enum: + - abandoned + - approved + - canceled + - declined + amount: + description: Amount of the transaction. + type: number + format: double + currency: + description: Currency of the transaction. + $ref: '#/components/schemas/CurrencyCode' + TransactionRefund: + type: object + required: + - amount + properties: + amount: + description: Amount of the refund. + type: number + format: double + description: + description: Description of the refund. + type: string + maxLength: 255 + isProcessedOutside: + description: Specifies if the refund is processed outside of Rebilly. + type: boolean + default: false + TransactionTimeline: + type: object + properties: + id: + type: string + description: ID of the timeline message. + readOnly: true + maxLength: 50 + example: tmln_0YV8Q9WEF5DTA8HFXS27D1G6GC + type: + description: Type of timeline message. + type: string + readOnly: true + enum: + - amount-adjusted + - blocklist-matched + - bump-offer-accepted + - bump-offer-presented + - bump-offer-rejected + - customer-redirected-offsite + - customer-returned + - dcc-offer-accepted + - dcc-offer-forced + - dcc-offer-presented + - dcc-offer-rejected + - dispute-changed + - dispute-created + - dispute-forfeited + - dispute-lost + - dispute-responded + - dispute-won + - gateway-connection-failed + - gateway-connection-timed-out + - gateway-response-received + - offsite-transaction-completed + - quickbooks-payment-created + - quickbooks-refund-receipt-created + - quickbooks-transaction-task-failed + - risk-score-changed + - timeline-comment-created + - transaction-abandoned + - transaction-amount-discrepancy-found + - transaction-approved + - transaction-canceled + - transaction-capture-delayed + - transaction-captured + - transaction-declined + - transaction-discrepancy-found + - transaction-disputed + - transaction-initiated + - transaction-payment-method-changed + - transaction-process-requested + - transaction-processed + - transaction-queried + - transaction-reconciled + - transaction-refunded + - transaction-retried + - transaction-rules-processed + - transaction-scheduled-time-changed + - transaction-timeout-resolved + - transaction-updated + - transaction-voided + - transaction-waiting-gateway + - transaction-waiting-gateway-completed + triggeredBy: + description: 'Specifies who, or what, triggered the timeline message.' + type: string + readOnly: true + enum: + - rebilly + - app + - direct-api + message: + description: Contents of the timeline message. + type: string + extraData: + $ref: '#/components/schemas/TimelineExtraData' + occurredTime: + description: Date and time when the timeline message occurred. + readOnly: true + $ref: '#/components/schemas/ServerTimestamp' + _links: + $ref: '#/components/schemas/SelfLink' + FixedFeeFormula: + type: object + required: + - type + - currency + - amount + properties: + type: + description: Type of fee. + type: string + enum: + - fixed-fee + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Amount of the fee. + type: number + format: double + example: 10 + x-type: Money + Bips: + type: number + description: 'Basis points. Example: 100 bips = 1%, 1 bip = 0.01%.' + format: integer + minimum: 0 + maximum: 100000 + example: 100 + PercentageFeeFormula: + type: object + required: + - type + - currency + - bips + properties: + type: + description: Type of fee. + type: string + enum: + - percentage + currency: + $ref: '#/components/schemas/CurrencyCode' + bips: + $ref: '#/components/schemas/Bips' + minAmount: + description: >- + Minimum fee amount. + + This value is applied when the calculated fee is less than the + required minimum. + type: number + format: double + example: 10 + x-type: Money + FeeFormula: + type: object + description: Formula that is used to calculate the fee. + discriminator: + propertyName: type + mapping: + fixed-fee: '#/components/schemas/FixedFeeFormula' + percentage: '#/components/schemas/PercentageFeeFormula' + anyOf: + - $ref: '#/components/schemas/FixedFeeFormula' + - $ref: '#/components/schemas/PercentageFeeFormula' + SettlementPeriod: + type: object + description: |- + Instruction for calculating the settlement time. + The settlement time and settlement period anchor are used + in conjunction with the transaction processed time, + to calculate the time in which the amount settles. + discriminator: + propertyName: method + mapping: + date-interval: '#/components/schemas/SchedulingMethodDateInterval' + immediately: '#/components/schemas/SchedulingMethodImmediately' + anyOf: + - $ref: '#/components/schemas/SchedulingMethodDateInterval' + - $ref: '#/components/schemas/SchedulingMethodImmediately' + SettlementSettings: + type: object + description: |- + Settlement settings. + Use these settings to set up settlement accounts. + required: + - settlementCurrency + - settlementPeriod + properties: + settlementCurrency: + description: Default settlement currency for all transactions. + $ref: '#/components/schemas/CurrencyCode' + settlementPeriod: + description: Default settlement period for all transactions. + $ref: '#/components/schemas/SettlementPeriod' + advancedSettings: + description: |- + Advanced settlement settings. + Use these settings to override either default settlement currency, + period, or both for the transactions matched a filter. + type: array + items: + type: object + required: + - filter + properties: + filter: + type: string + description: >- + Filter that is based on transaction properties and is used to + determine when to apply the settings. + minLength: 1 + maxLength: 255 + example: 'currency:EUR,GBP' + settlementCurrency: + $ref: '#/components/schemas/CurrencyCode' + settlementPeriod: + $ref: '#/components/schemas/SettlementPeriod' + Fee: + type: object + required: + - type + - name + - formula + properties: + id: + type: string + description: ID of the fee. + readOnly: true + maxLength: 50 + example: fee_01GQT145JX3XBRJ8K812Y3GRE9 + type: + description: Type of fee. + type: string + enum: + - buy + - sell + name: + type: string + description: Name of the fee. + minLength: 1 + maxLength: 255 + example: A gateway fee + filter: + description: >- + Filter that is based on the properties of the transaction and used + to determine when to apply the fee. + minLength: 1 + maxLength: 255 + example: 'type:sale,capture;result:approved' + type: + - string + - 'null' + formula: + $ref: '#/components/schemas/FeeFormula' + settlementSettings: + description: >- + Fee settlement settings. + + This value overrides the gateway account financial settings of the + transaction. + oneOf: + - $ref: '#/components/schemas/SettlementSettings' + - type: 'null' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + PatchFee: + type: object + properties: + type: + description: Type of fee. + type: string + enum: + - buy + - sell + name: + type: string + description: Name of the fee. + minLength: 1 + maxLength: 255 + example: A gateway fee + filter: + description: >- + Filter that is based on the properties of the transaction and used + to determine when to apply the fee. + minLength: 1 + maxLength: 255 + example: 'type:sale,capture;result:approved' + type: + - string + - 'null' + formula: + $ref: '#/components/schemas/FeeFormula' + settlementSettings: + description: >- + Fee settlement settings. + + This value overrides the gateway account financial settings of the + transaction. + oneOf: + - $ref: '#/components/schemas/SettlementSettings' + - type: 'null' + AllowedIps: + description: |- + List of IP addresses that are permitted access. + Private subnets are prohibited. + To remove restrictions, set this value to `null`. + type: + - array + - 'null' + items: + type: string + format: ip + example: + - 153.12.32.33 + - 201.54.122.0/24 + - 76.*.*.* + - '2001:0db8:abcd:0012:0000:0000:0000:ffff' + - '2001:db8:abcd:12::0/64' + ApiKey: + type: object + description: API secret Key. + required: + - description + properties: + id: + type: string + readOnly: true + description: ID of the API key. + maxLength: 50 + example: api_key_0YV7JQMY6ED2FBE58BMFHBWBZH + description: + description: API key description. + type: string + type: + description: Type of API key. + type: string + default: secret + enum: + - secret + - publishable + acl: + description: >- + Specify access control list here if creating a restricted API key. + Send all matching permission with an empty scope to allow all + permissions. + $ref: '#/components/schemas/Acl' + allowedIps: + $ref: '#/components/schemas/AllowedIps' + apiUser: + description: API user name. + type: string + readOnly: true + secretKey: + description: API secret key's value. + type: string + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + ChargeTransaction: + type: object + required: + - type + properties: + id: + type: string + description: ID of the balance transaction. + readOnly: true + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + type: + description: Type of balance transaction. + type: string + readOnly: true + enum: + - charge + parentId: + description: >- + ID of the parent entry if one exists. + + + Some types of transactions relate to parent transactions. + + For example, a `risk-reserve` relates to a `charge`, and a + `risk-reserve-release` relates a to `risk-reserve`. + type: + - string + - 'null' + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + transactionId: + description: ID of the related transaction. + $ref: '#/components/schemas/TransactionId' + settlementCurrency: + $ref: '#/components/schemas/CurrencyCode' + settlementAmount: + x-type: Money + x-currency-field: settlementCurrency + x-sortable: true + x-basic: true + description: Amount of the settlement. + type: + - number + - 'null' + format: double + settlementTime: + description: Date and time of the expected settlement. + $ref: '#/components/schemas/ServerTimestamp' + settlementRate: + description: |- + If the settlement currency does not match the transaction currency. + This field displays the exchange rate of the settlement. + type: + - number + - 'null' + format: double + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + RefundTransaction: + type: object + required: + - type + properties: + id: + type: string + description: ID of the balance transaction. + readOnly: true + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + type: + description: Type of balance transaction. + type: string + readOnly: true + enum: + - refund + parentId: + description: >- + ID of the parent entry if one exists. + + + Some types of transactions relate to parent transactions. + + For example, a `risk-reserve` relates to a `charge`, and a + `risk-reserve-release` relates a to `risk-reserve`. + type: + - string + - 'null' + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + transactionId: + description: ID of the related transaction. + $ref: '#/components/schemas/TransactionId' + settlementCurrency: + $ref: '#/components/schemas/CurrencyCode' + settlementAmount: + x-type: Money + x-currency-field: settlementCurrency + x-sortable: true + x-basic: true + description: Amount of the settlement. + type: + - number + - 'null' + format: double + settlementTime: + description: Date and time of the expected settlement. + $ref: '#/components/schemas/ServerTimestamp' + settlementRate: + description: |- + If the settlement currency does not match the transaction currency. + This field displays the exchange rate of the settlement. + type: + - number + - 'null' + format: double + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + BuyFeeTransaction: + type: object + required: + - type + properties: + id: + type: string + description: ID of the balance transaction. + readOnly: true + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + type: + description: Type of balance transaction. + type: string + readOnly: true + enum: + - buy-fee + fee: + type: object + properties: + name: + description: Name of the fee. + type: string + parentId: + description: >- + ID of the parent entry if one exists. + + + Some types of transactions relate to parent transactions. + + For example, a `risk-reserve` relates to a `charge`, and a + `risk-reserve-release` relates a to `risk-reserve`. + type: + - string + - 'null' + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + transactionId: + description: ID of the related transaction. + $ref: '#/components/schemas/TransactionId' + settlementCurrency: + $ref: '#/components/schemas/CurrencyCode' + settlementAmount: + x-type: Money + x-currency-field: settlementCurrency + x-sortable: true + x-basic: true + description: Amount of the settlement. + type: + - number + - 'null' + format: double + settlementTime: + description: Date and time of the expected settlement. + $ref: '#/components/schemas/ServerTimestamp' + settlementRate: + description: |- + If the settlement currency does not match the transaction currency. + This field displays the exchange rate of the settlement. + type: + - number + - 'null' + format: double + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + SellFeeTransaction: + type: object + required: + - type + properties: + id: + type: string + description: ID of the balance transaction. + readOnly: true + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + type: + description: Type of balance transaction. + type: string + readOnly: true + enum: + - sell-fee + fee: + type: object + properties: + name: + description: Name of the fee. + type: string + parentId: + description: >- + ID of the parent entry if one exists. + + + Some types of transactions relate to parent transactions. + + For example, a `risk-reserve` relates to a `charge`, and a + `risk-reserve-release` relates a to `risk-reserve`. + type: + - string + - 'null' + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + transactionId: + description: ID of the related transaction. + $ref: '#/components/schemas/TransactionId' + settlementCurrency: + $ref: '#/components/schemas/CurrencyCode' + settlementAmount: + x-type: Money + x-currency-field: settlementCurrency + x-sortable: true + x-basic: true + description: Amount of the settlement. + type: + - number + - 'null' + format: double + settlementTime: + description: Date and time of the expected settlement. + $ref: '#/components/schemas/ServerTimestamp' + settlementRate: + description: |- + If the settlement currency does not match the transaction currency. + This field displays the exchange rate of the settlement. + type: + - number + - 'null' + format: double + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + RiskReserveTransaction: + type: object + required: + - type + properties: + id: + type: string + description: ID of the balance transaction. + readOnly: true + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + type: + description: Type of balance transaction. + type: string + readOnly: true + enum: + - risk-reserve + riskReserve: + type: object + properties: + releaseTime: + description: Time of risk reserve release. + type: + - string + - 'null' + format: date-time + readOnly: true + parentId: + description: >- + ID of the parent entry if one exists. + + + Some types of transactions relate to parent transactions. + + For example, a `risk-reserve` relates to a `charge`, and a + `risk-reserve-release` relates a to `risk-reserve`. + type: string + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + transactionId: + description: ID of the related transaction. + $ref: '#/components/schemas/TransactionId' + settlementCurrency: + $ref: '#/components/schemas/CurrencyCode' + settlementAmount: + x-type: Money + x-currency-field: settlementCurrency + x-sortable: true + x-basic: true + description: Amount of the settlement. + type: + - number + - 'null' + format: double + settlementTime: + description: Date and time of the expected settlement. + $ref: '#/components/schemas/ServerTimestamp' + settlementRate: + description: |- + If the settlement currency does not match the transaction currency. + This field displays the exchange rate of the settlement. + type: + - number + - 'null' + format: double + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + RiskReserveReleaseTransaction: + type: object + required: + - type + properties: + id: + type: string + description: ID of the balance transaction. + readOnly: true + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + type: + description: Type of balance transaction. + type: string + readOnly: true + enum: + - risk-reserve-release + parentId: + description: >- + ID of the parent entry if one exists. + + + Some types of transactions relate to parent transactions. + + For example, a `risk-reserve` relates to a `charge`, and a + `risk-reserve-release` relates a to `risk-reserve`. + type: string + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + transactionId: + description: ID of the related transaction. + $ref: '#/components/schemas/TransactionId' + settlementCurrency: + $ref: '#/components/schemas/CurrencyCode' + settlementAmount: + x-type: Money + x-currency-field: settlementCurrency + x-sortable: true + x-basic: true + description: Amount of the settlement. + type: + - number + - 'null' + format: double + settlementTime: + description: Date and time of the expected settlement. + $ref: '#/components/schemas/ServerTimestamp' + settlementRate: + description: |- + If the settlement currency does not match the transaction currency. + This field displays the exchange rate of the settlement. + type: + - number + - 'null' + format: double + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + ReverseTransaction: + type: object + required: + - type + properties: + id: + type: string + description: ID of the balance transaction. + readOnly: true + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + type: + description: Type of balance transaction. + type: string + readOnly: true + enum: + - reverse + parentId: + description: >- + ID of the parent entry if one exists. + + + Some types of transactions relate to parent transactions. + + For example, a `risk-reserve` relates to a `charge`, and a + `risk-reserve-release` relates a to `risk-reserve`. + type: string + maxLength: 50 + example: btxn_0YVCNJYRXTDDQ9QF2Z60ZA05WP + transactionId: + description: ID of the related transaction. + $ref: '#/components/schemas/TransactionId' + settlementCurrency: + $ref: '#/components/schemas/CurrencyCode' + settlementAmount: + x-type: Money + x-currency-field: settlementCurrency + x-sortable: true + x-basic: true + description: Amount of the settlement. + type: + - number + - 'null' + format: double + settlementTime: + description: Date and time of the expected settlement. + $ref: '#/components/schemas/ServerTimestamp' + settlementRate: + description: |- + If the settlement currency does not match the transaction currency. + This field displays the exchange rate of the settlement. + type: + - number + - 'null' + format: double + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + BalanceTransaction: + type: object + title: Balance transaction + discriminator: + propertyName: type + mapping: + charge: '#/components/schemas/ChargeTransaction' + refund: '#/components/schemas/RefundTransaction' + buy-fee: '#/components/schemas/BuyFeeTransaction' + sell-fee: '#/components/schemas/SellFeeTransaction' + risk-reserve: '#/components/schemas/RiskReserveTransaction' + risk-reserve-release: '#/components/schemas/RiskReserveReleaseTransaction' + reverse: '#/components/schemas/ReverseTransaction' + oneOf: + - $ref: '#/components/schemas/ChargeTransaction' + - $ref: '#/components/schemas/RefundTransaction' + - $ref: '#/components/schemas/BuyFeeTransaction' + - $ref: '#/components/schemas/SellFeeTransaction' + - $ref: '#/components/schemas/RiskReserveTransaction' + - $ref: '#/components/schemas/RiskReserveReleaseTransaction' + - $ref: '#/components/schemas/ReverseTransaction' + BillingPortal: + type: object + required: + - websiteId + - slug + - name + properties: + id: + type: string + description: ID of the billing portal. + readOnly: true + maxLength: 50 + example: bill_prt_0YV7K5TYV5D1P9ZNKDT39KZC3C + websiteId: + $ref: '#/components/schemas/WebsiteId' + slug: + description: >- + Path segment that is appended to the billing portal URL to help make + it human-readable. + + Example: `https://example.com/billing-portal/{slug}`. + type: string + minLength: 5 + maxLength: 100 + name: + description: >- + Billing portal name. + + This name is for internal use and helps you to organize and + differentiate billing portals. + + This value is not displayed to your customers. + type: string + customDomain: + description: |- + Custom domain for the billing portal. + The default domain is: `portal.secure-payments.app`. + type: + - string + - 'null' + maxLength: 255 + features: + description: Features that can be enabled for the billing portal. + type: object + properties: + authenticateWithPassword: + description: Specifies if a customer can authenticate with a password. + type: boolean + default: true + orderCancel: + description: Specifies if a customer can cancel an order. + type: boolean + default: true + orderAddressEdit: + description: Specifies if a customer can change an order address. + type: boolean + default: true + paymentInstrumentAdd: + description: Specifies if a customer can add a new payment instrument. + type: boolean + default: true + paymentInstrumentUpdate: + description: Specifies if a customer can update their payment instruments. + type: boolean + default: true + paymentInstrumentDeactivate: + description: Specifies if a customer can disable their payment instruments. + type: boolean + default: true + customization: + description: Visual customization options for the billing portal. + type: object + properties: + logoId: + type: + - string + - 'null' + description: File ID of the billing portal logo. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + colors: + description: Various colors used in the billing portal. + type: object + properties: + primary: + description: Primary color for the billing portal in hexadecimal format. + type: string + maxLength: 6 + default: 0044d4 + secondary: + description: >- + Secondary color for the billing portal in hexadecimal + format. + type: string + maxLength: 6 + default: ffffff + links: + description: URLs that are displayed in the billing portal. + type: object + properties: + refundPolicy: + description: Website refund policy URL. + type: + - string + - 'null' + format: url + privacyPolicy: + description: Website privacy policy URL. + type: + - string + - 'null' + format: url + termsOfService: + description: Website terms of service URL. + type: + - string + - 'null' + format: url + status: + description: |- + Status of the billing portal. If the status is `inactive`, + the billing portal URL results in a 404 error. + type: string + enum: + - active + - inactive + default: active + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + BroadcastMessage: + type: object + required: + - startSendingTime + - messages + properties: + id: + type: string + description: ID of the broadcast message. + readOnly: true + maxLength: 50 + example: mail_bcst_0YV8XW5EXWDVEAP64XP8CY2X40 + filter: + type: + - string + - 'null' + description: >- + Use this filter to select customers during broadcast message + processing. + + + This field uses the same syntax as the `query` filter, + + but without the `filter=` prefix. Examples: `firstName:John`, + `firstName:John;lastName:Doe`. + + + > **Important**: Do not URL encode the filter value. + example: 'firstName:John;lastName:Doe' + title: + type: string + description: Title of the message. This title displays in reports. + messages: + type: array + description: List of messages. + minItems: 1 + items: + type: object + properties: + id: + type: string + format: uuid + description: ID of the message. + version: + type: string + description: >- + Version number of the message. + + Use this field to distinguish between multiple messages by + name and version number. + + If there are no versions, this field is empty. + weight: + type: integer + description: >- + Weight distribution value that is assigned to a template for a + split test. + + Each template in a split test can be assigned a weight. + + The higher the weight value, the more likely the message + template is used. + + + The split test algorithm does not assess locale when making a + weighted template selection. + minimum: 0 + maximum: 100 + example: 75 + default: 100 + templates: + description: >- + Split test templates. + + + Use split tests to determine which content is most effective. + + Split tests send variations of the same message to different + customers or leads. + + You can then send the winning message to the remaining + customers or leads. + type: array + minItems: 1 + items: + type: object + properties: + from: + type: string + description: >- + Email address of the sender. + + + > **Important:** This value must be a verified email + address. + + + To verify an email address: + 1. [Create an email delivery setting](../../Email-delivery-settings/operation/PostEmailDeliverySetting). + In the response, you receive the email and a token. + 1. [Verify the email delivery](../../Email-delivery-settings/operation/PutEmailDeliverySettingsVerification) by passing the token as the path parameter. + maxLength: 254 + example: example@example.com + subject: + type: string + description: >- + Subject of the message. + + The use of template placeholders is permitted for this + field. + maxLength: 998 + example: Demonstration subject + text: + type: string + description: >- + Text body of the message. + + To use content from the `html` field, leave this field + empty. + + The use of template placeholders is permitted for this + field. + example: Demonstration text + html: + type: string + description: >- + HTML body of the message. + + To use content from the `text` field, leave this field + empty. + + The use of template placeholders is permitted for this + field. + example:

Demonstration text

+ locale: + type: string + description: >- + Language locale identifier in [RFC + 5646](https://tools.ietf.org/html/rfc5646) format. + example: fr-FR + required: + - from + - subject + - text + - html + - locale + required: + - templates + splitTestStartTime: + readOnly: true + type: + - string + - 'null' + format: date-time + description: Date and time when the split test messages are scheduled to send. + startSendingTime: + type: string + format: date-time + description: Date and time when the message is scheduled to send. + status: + readOnly: true + type: string + description: Status of the broadcast message. + enum: + - draft + - sending + - sent + - archived + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + DepositRequest: + type: object + required: + - websiteId + - customerId + - currency + properties: + id: + type: string + description: ID of the deposit request. + readOnly: true + maxLength: 50 + example: dep_req_0YVJ65BSGYC3EAT58SEX8KY6J7 + websiteId: + description: >- + Website ID of the deposit. This value specifies the website with + which the deposit is associated. + type: string + maxLength: 50 + example: web_0YV7DE4Z26DQSA1AC92FBJ7SEG + customerId: + $ref: '#/components/schemas/CustomerId' + transactionId: + type: + - string + - 'null' + description: ID of the transaction that is used in the deposit request. + readOnly: true + maxLength: 50 + example: txn_0YVDTQJ8YWDGQACV2N2N5SPWQ0 + status: + description: Status of the request. + type: string + readOnly: true + enum: + - created + - pending + - completed + - expired + x-enumDescriptions: + created: >- + Request is created, but it has not been visited by a customer. + This is a temporary state. + pending: >- + Request has been visited by a customer, but no funds have been + deposited yet. This is a temporary state. + completed: >- + A funds deposit transaction has been initiated. This is a + permanent state. + expired: >- + Request expired without a deposit attempt. This is a permanent + state. + currency: + $ref: '#/components/schemas/CurrencyCode' + amounts: + type: array + description: >- + List of available deposit amounts. + + + If `amounts` is not specified when a deposit is created, amounts are + determined from the chosen strategy. + + For more information, see the [`strategyId` + property](https://all-rebilly.redoc.ly/tag/Deposits/#operation/PostDepositRequest#!path=strategyId&t=request). + items: + type: number + format: double + minimum: 0.01 + customAmount: + type: + - object + - 'null' + description: >- + Custom amount restrictions. + + If this value is `null`, custom amounts are prohibited. + + If `customAmount` is not specified when a deposit request is + created, amount restrictions are determined from the chosen + strategy. + + For more information, see the [`strategyId` + property](https://all-rebilly.redoc.ly/tag/Deposits/#operation/PostDepositRequest#!path=strategyId&t=request). + required: + - minimum + - multipleOf + - maximum + properties: + minimum: + description: Minimum custom amount. + type: number + format: double + minimum: 0.01 + multipleOf: + description: >- + Multiple by which the custom amount increases. + + + For example, if `minimum` is equal to 5.30, and `multipleOf` is + 0.50, the valid custom amount is 5.30, 5.80, 6.30, 6.80 and so + on until the `maximum` value is reached. + + + A valid custom amount must be equal to `minimum` + X * + `multipleOf`, where X is any non negative integer. + type: number + format: double + minimum: 0.01 + maximum: + description: >- + Maximum custom amount. This value must be equal to `minimum` + X + * `multipleOf`, where X is any positive integer. + type: number + format: double + minimum: 0.01 + redirectUrl: + description: >- + URL to redirect the customer to when a deposit is completed. The + default value is the website URL. + type: string + format: uri + expirationTime: + description: >- + Date and time at which the deposit request expires. The default + expiration time is one hour from the time the request is created. + type: string + format: date-time + propertiesSchema: + readOnly: true + description: >- + Defines properties the user can complete when they use the hosted + deposit form. + + This field accepts [JSON-schema](https://json-schema.org/) drafts 4, + 6, and 7. + type: + - object + - 'null' + example: + type: object + properties: + email: + type: string + max: + type: integer + minimum: 0 + exclusiveMaximum: 100 + required: + - email + properties: + readOnly: true + description: >- + Properties that are available for the user to complete when they use + the hosted deposit form. + + Use this object to describe fields that are rendered and completed + on the hosted deposit form. + type: + - object + - 'null' + additionalProperties: + type: string + example: + email: email@example.com + max: '33' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - deposit + - transaction + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + website: + type: object + transaction: + type: object + PostDepositRequest: + type: object + required: + - websiteId + - customerId + - currency + properties: + websiteId: + description: >- + Website ID of the deposit. This value specifies the website with + which the deposit is associated. + type: string + maxLength: 50 + example: web_0YV7DE4Z26DQSA1AC92FBJ7SEG + customerId: + $ref: '#/components/schemas/CustomerId' + strategyId: + description: >- + ID of a strategy to be applied for this request for `amounts` and + `customAmount`. + + If this field is not specified, a randomly selected strategy with a + matching `filter` value is applied for empty `amounts` and + `customAmount`. + + If there is no matching strategy, the default strategy with the + following parameters is applied for empty `amounts` and + `customAmount`: + + ```yaml + amounts: + calculator: absolute + baseAmount: 10 + increments: [10, 20] + adjustBaseToLastDeposit: true + customAmount: + minimum: 1 + multipleOf: 1 + maximum: 10000 + ``` + + For more information, see [Create a deposit + strategy](https://all-rebilly.redoc.ly/tag/Deposits/#operation/PostDepositStrategy). + type: + - string + - 'null' + maxLength: 50 + example: dep_str_0YVJ64MAHTDPA97H8S7R5MYR1M + currency: + $ref: '#/components/schemas/CurrencyCode' + amounts: + type: + - array + - 'null' + description: >- + List of available deposit amounts. + + + If `amounts` is not specified when a deposit request is created, + amounts are determined from the chosen strategy. + + For more information, see the [`strategyId` + property](https://all-rebilly.redoc.ly/tag/Deposits/#operation/PostDepositRequest#!path=strategyId&t=request). + items: + type: number + format: double + minimum: 0.01 + amountLimits: + type: + - object + - 'null' + description: >- + Deposit amount limit information. Set optional minimum and maximum + deposit amounts. Limits override `amounts` and `customAmount` + values. + + If this value is `null`, deposit amount limits are not set. + minProperties: 1 + properties: + minimum: + description: Minimum deposit amount. + type: + - number + - 'null' + format: double + default: 0 + minimum: 0 + maximum: + description: Maximum deposit amount. + type: + - number + - 'null' + format: double + minimum: 0 + customAmount: + type: + - object + - 'null' + description: >- + Custom amount restrictions. + + If this value is `null`, custom amounts are prohibited. + + If `customAmount` is not specified when a deposit request is + created, amount restrictions are determined from the chosen + strategy. + + For more information, see the [`strategyId` + property](https://all-rebilly.redoc.ly/tag/Deposits/#operation/PostDepositRequest#!path=strategyId&t=request). + required: + - minimum + - multipleOf + - maximum + properties: + minimum: + description: Minimum custom amount. + type: number + format: double + minimum: 0.01 + multipleOf: + description: >- + Multiple by which the custom amount increases. + + + For example, if `minimum` is equal to 5.30, and `multipleOf` is + 0.50, the valid custom amount is 5.30, 5.80, 6.30, 6.80 and so + on until the `maximum` value is reached. + + + A valid custom amount must be equal to `minimum` + X * + `multipleOf`, where X is any non negative integer. + type: number + format: double + minimum: 0.01 + maximum: + description: >- + Maximum custom amount. This value must be equal to `minimum` + X + * `multipleOf`, where X is any positive integer. + type: number + format: double + minimum: 0.01 + redirectUrl: + description: >- + URL to redirect the customer to when a deposit is completed. The + default value is the website URL. + type: + - string + - 'null' + format: uri + expirationTime: + description: >- + Date and time at which the deposit request expires. The default + expiration time is one hour after the time the request is created. + type: + - string + - 'null' + format: date-time + customPropertySetId: + description: >- + ID of a custom property set to apply to the request + `propertiesSchema`. + type: + - string + - 'null' + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + DepositStrategy: + type: object + required: + - name + - amounts + - customAmount + properties: + id: + type: string + description: ID of the deposit strategy. + readOnly: true + maxLength: 50 + example: dep_str_0YVJ64MAHTDPA97H8S7R5MYR1M + name: + type: string + description: Name of the strategy. + filter: + type: string + description: >- + Filter that uses deposit request properties to determine if a + deposit strategy is applicable for the request. + + If this field is empty, the strategy is applicable for any request. + default: '' + example: 'depositRequest.currency:USD' + amounts: + type: object + description: Deposit strategy amounts configuration. + required: + - calculator + - baseAmount + - increments + properties: + calculator: + type: string + description: >- + Deposit amounts calculator. + + This field specifies if amounts are calculated using an absolute + or percentage calculation. + + + Amounts are specified in `baseAmount`, and increments are + specified in `increments`. + + + Example: `baseAmount: 10` and `increments: [20, 50, 100]`. + + + - If the calculator is set to `absolute`: the amounts displayed + to the customer are `[10, 30, 60, 110]`. + + - If the calculator is set to `percent`: the amounts displayed + to the customer are `[10, 12, 15, 20]`. + enum: + - absolute + - percent + baseAmount: + type: number + format: double + minimum: 0.01 + description: First amount that is displayed to customer. + increments: + type: array + description: >- + List of incremental amounts that are displayed to customer. + + + For more information, see the [`calculator` + property](https://all-rebilly.redoc.ly/tag/Deposits/#operation/PostDepositStrategy#!path=amounts/calculator&t=request). + items: + type: number + format: double + minimum: 0.01 + adjustBaseToLastDeposit: + type: boolean + default: false + description: >- + Specifies if the base amount must be adjusted based on the last + deposit. + + If this value is `true`, the `baseAmount` is changed to the last + successful deposit amount made using [Create a deposit + request](https://all-rebilly.redoc.ly/tag/Deposits/#operation/PostDepositRequest). + + + If the customer has no successful deposits, `baseAmount` is not + adjusted. + customAmount: + type: + - object + - 'null' + description: >- + Custom amount restrictions. + + If this value is `null`, custom amounts are prohibited. + + If `customAmount` is not specified when a deposit request is + created, amount restrictions are determined from the chosen + strategy. + + For more information, see the [`strategyId` + property](https://all-rebilly.redoc.ly/tag/Deposits/#operation/PostDepositRequest#!path=strategyId&t=request). + required: + - minimum + - multipleOf + - maximum + properties: + minimum: + description: Minimum custom amount. + type: number + format: double + minimum: 0.01 + multipleOf: + description: >- + Multiple by which the custom amount increases. + + + For example, if `minimum` is equal to 5.30, and `multipleOf` is + 0.50, the valid custom amount is 5.30, 5.80, 6.30, 6.80 and so + on until the `maximum` value is reached. + + + A valid custom amount must be equal to `minimum` + X * + `multipleOf`, where X is any non negative integer. + type: number + format: double + minimum: 0.01 + maximum: + description: >- + Maximum custom amount. This value must be equal to `minimum` + X + * `multipleOf`, where X is any positive integer. + type: number + format: double + minimum: 0.01 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + DepositCustomPropertySet: + type: object + required: + - name + - properties + properties: + id: + type: string + description: ID of the deposit property set. + readOnly: true + maxLength: 50 + example: dep_prop_0YVJ640MB4CXVB8KXBRW3B79R9 + name: + type: string + description: Name of the custom property set. + properties: + description: >- + Defines properties the user can complete when they use the hosted + deposit form. + + This field accepts [JSON-schema](https://json-schema.org/) drafts 4, + 6, and 7. + + Accepted properties types are: `string`, `number`, `integer`, + `array`, and `enum`. + + For more information, see [Configure custom hosted deposit + properties](https://www.rebilly.com/docs/dev-docs/configure-custom-hosted-deposit-properties/). + type: object + example: + type: object + properties: + email: + type: string + max: + type: integer + minimum: 0 + exclusiveMaximum: 100 + required: + - email + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + CheckoutFormFixedPlan: + title: Fixed quantity + type: object + required: + - planId + - type + - quantity + properties: + planId: + type: string + description: ID of the plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + type: + description: |- + Type of plan. If this value is set to `fixed`, + customers cannot choose the plan quantity. + If this value is set to `variable`, + customers can choose the plan quantity. + type: string + enum: + - fixed + quantity: + description: Quantity of the plan. + type: integer + minimum: 1 + CheckoutFormVariablePlan: + title: Variable quantity + type: object + required: + - planId + - type + - quantity + properties: + planId: + type: string + description: ID of the plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + type: + description: |- + Type of plan. If this value is set to `variable`, + customers can choose the plan quantity. + If this value is set to `fixed`, + customers cannot choose the plan quantity. + type: string + enum: + - variable + quantity: + description: Default plan quantity. + type: integer + minimum: + description: Minimum plan quantity. + type: integer + minimum: 1 + default: 1 + multipleOf: + description: Multiple by which the plan quantity increases. + type: integer + minimum: 1 + default: 1 + maximum: + description: Maximum plan quantity. + type: integer + minimum: 1 + default: 100 + CheckoutFormPlan: + type: object + discriminator: + propertyName: type + mapping: + fixed: '#/components/schemas/CheckoutFormFixedPlan' + variable: '#/components/schemas/CheckoutFormVariablePlan' + oneOf: + - $ref: '#/components/schemas/CheckoutFormFixedPlan' + - $ref: '#/components/schemas/CheckoutFormVariablePlan' + CheckoutForm: + type: object + required: + - name + - websiteId + - plans + properties: + id: + type: string + description: ID of the checkout form. + readOnly: true + maxLength: 50 + example: chkt_frm_0YV8XZ6174C2MBS5011SAZNMBP + websiteId: + $ref: '#/components/schemas/WebsiteId' + customDomain: + description: Custom domain for the checkout form. + type: + - string + - 'null' + maxLength: 255 + plans: + type: array + description: >- + List of plans that are applied to a customer order by default. + + Plans describe how the customer must pay for products. For more + information, see + [Plans](https://www.rebilly.com/docs/dev-docs/api/tag/Plans/). + minItems: 1 + items: + $ref: '#/components/schemas/CheckoutFormPlan' + addonPlans: + type: array + description: >- + List of add-on plans that are displayed to the customer on the + payment screen. + + Add-ons are plans that the customer has not already subscribed to. + + + The customer selects whether to add an add-on plan to their current + order. + default: [] + items: + $ref: '#/components/schemas/CheckoutFormPlan' + bumpPlans: + type: array + description: >- + List of bump plans that are displayed to the customer on the payment + screen. + + Use bump plans to offer purchase bonuses, discounts, and deals to + the customer. + + + The customer selects whether to purchase bump plans, + + or to use the plans that are specified in their current order. + default: [] + items: + $ref: '#/components/schemas/CheckoutFormPlan' + accountsEnabled: + description: >- + Specifies if the account is enabled. + + If this value is `true`, the customer can sign-up and sign-in using + the checkout form. + type: boolean + default: false + couponsEnabled: + description: >- + Specifies if coupons are enabled in the checkout form. + + If this value is `true`, the customer can use coupons in the + checkout form. + + Use coupons to reward customers, generate sales, or to test new + pricing strategies. + type: boolean + default: false + purchaseLimit: + description: >- + Limits the number of purchases that can be made using a specific + checkout form. + + If a purchase limit value is set, each purchase decreases this + value. + + When the purchases limit value reaches zero, the checkout form + becomes inactive. + type: + - integer + - 'null' + minimum: 0 + default: null + paymentMethods: + description: |- + List of available payment methods. + Payment methods must have at least one active gateway account. + If not specified, all available payment methods are displayed. + type: array + items: + $ref: '#/components/schemas/PaymentMethod' + customization: + description: Visual customization options for the checkout form. + type: object + properties: + logoId: + description: ID of the linked file object. + type: + - string + - 'null' + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + summary: + description: Summary text. + type: + - string + - 'null' + buttonText: + description: >- + Button text. Use the `{{amount}}` placeholder to display the + checkout form total. + type: string + default: 'Pay {{amount}}' + colors: + description: Primary color used in the checkout form and button text. + type: object + properties: + primary: + description: Primary color for the checkout form in hexadecimal format. + type: string + maxLength: 6 + default: 0044d4 + buttonText: + description: >- + Button text color for the checkout form in hexadecimal + format. + type: string + maxLength: 6 + default: ffffff + links: + description: URLs that are displayed on the checkout form. + type: object + properties: + refundPolicy: + description: Website refund policy URL. + type: + - string + - 'null' + format: url + privacyPolicy: + description: Website privacy policy URL. + type: + - string + - 'null' + format: url + termsOfService: + description: Website terms of service URL. + type: + - string + - 'null' + format: url + tracking: + description: Tracking system IDs. + type: object + properties: + googleAnalytics: + description: Google Analytics tracking ID. + type: + - string + - 'null' + example: UA-XXXXX-YY + googleTagManager: + description: Google Tag Manager tracking ID. + type: + - string + - 'null' + example: GTM-XXXXX + gtagJs: + description: >- + Google Analytics tracking ID. + + This value is used by Google Global Site Tag (gtag.js) + service. + type: + - string + - 'null' + example: UA-XXXXX-YY + facebookPixel: + description: Facebook Pixel tracking ID. + type: + - string + - 'null' + example: '1234567890' + segmentAnalytics: + description: Segment Analytics tracking ID. + type: + - string + - 'null' + example: '1234567890' + heapIo: + description: Heap.io tracking ID. + type: + - string + - 'null' + example: '1234567890' + requiredAdditionalFields: + description: List of required fields. + type: array + items: + type: string + example: + - information.companyName + - information.phoneNumber + name: + description: >- + Name of the checkout form. + + This name value is for internal display, and is not visible to your + customers. + + Use this name to locate and organize your checkout forms. + type: string + status: + description: |- + Status of the checkout form. If the `status` value is `inactive`, + the checkout form URL produces a 404 error. + type: string + enum: + - active + - inactive + default: active + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - preview + SmtpAuthorizationNone: + type: object + required: + - type + properties: + type: + type: string + enum: + - none + default: none + SmtpAuthorizationCramMd5: + type: object + properties: + username: + description: Authentication username. + type: string + password: + description: Authentication password. + type: string + format: password + type: + type: string + enum: + - cram-md5 + default: none + required: + - username + - password + - type + SmtpAuthorizationLogin: + type: object + properties: + username: + description: Authentication username. + type: string + password: + description: Authentication password. + type: string + format: password + type: + type: string + enum: + - login + default: none + required: + - username + - password + - type + SmtpAuthorizationPlain: + type: object + properties: + username: + description: Authentication username. + type: string + password: + description: Authentication password. + type: string + format: password + type: + type: string + enum: + - plain + default: none + required: + - username + - password + - type + SmtpAuthorization: + type: object + description: Authentication type and details. + discriminator: + propertyName: type + mapping: + none: '#/components/schemas/SmtpAuthorizationNone' + cram-md5: '#/components/schemas/SmtpAuthorizationCramMd5' + login: '#/components/schemas/SmtpAuthorizationLogin' + plain: '#/components/schemas/SmtpAuthorizationPlain' + oneOf: + - $ref: '#/components/schemas/SmtpAuthorizationNone' + - $ref: '#/components/schemas/SmtpAuthorizationCramMd5' + - $ref: '#/components/schemas/SmtpAuthorizationLogin' + - $ref: '#/components/schemas/SmtpAuthorizationPlain' + SmtpCredential: + type: object + description: SMTP credential. + required: + - host + - type + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + hash: + deprecated: true + type: string + description: ID of the credential. Use `id` field instead. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + deactivationTime: + description: Date and time when the credential is deactivated. + type: + - string + - 'null' + format: date-time + readOnly: true + type: + description: Type of credential. + type: string + readOnly: true + enum: + - smtp + host: + type: string + description: SMTP host name. + port: + type: integer + description: SMTP port value. + minimum: 1 + maximum: 65535 + default: 25 + encryption: + type: string + description: Encryption value. + enum: + - none + - tls + - ssl + default: none + auth: + $ref: '#/components/schemas/SmtpAuthorization' + _links: + $ref: '#/components/schemas/SelfLink' + WebhookAuthorizationNone: + type: object + properties: + type: + type: string + description: Type of authorization. + enum: + - none + default: none + required: + - type + WebhookAuthorizationBasic: + type: object + properties: + type: + type: string + description: Type of authorization. + enum: + - basic + default: none + username: + description: Authentication username. + type: string + password: + description: Authentication password. + type: string + format: password + required: + - username + - password + - type + WebhookAuthorizationDigest: + type: object + properties: + type: + type: string + description: Type of authorization. + enum: + - digest + default: none + username: + description: Authentication username. + type: string + password: + description: Authentication password. + type: string + format: password + required: + - username + - password + - type + WebhookAuthorizationOAuth1: + type: object + properties: + type: + type: string + description: Type of authorization. + enum: + - oauth1 + default: none + consumerKey: + description: Consumer key. + type: string + consumerSecret: + description: Customer secret. + type: string + token: + description: Authentication token. + type: string + tokenSecret: + description: Secret token. + type: string + required: + - consumerKey + - consumerSecret + - token + - tokenSecret + - type + WebhookAuthorization: + type: object + description: Authentication type and details. + discriminator: + propertyName: type + mapping: + none: '#/components/schemas/WebhookAuthorizationNone' + basic: '#/components/schemas/WebhookAuthorizationBasic' + digest: '#/components/schemas/WebhookAuthorizationDigest' + oauth1: '#/components/schemas/WebhookAuthorizationOAuth1' + oneOf: + - $ref: '#/components/schemas/WebhookAuthorizationNone' + - $ref: '#/components/schemas/WebhookAuthorizationBasic' + - $ref: '#/components/schemas/WebhookAuthorizationDigest' + - $ref: '#/components/schemas/WebhookAuthorizationOAuth1' + WebhookCredential: + type: object + description: Webhook credential. + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + hash: + deprecated: true + type: string + description: ID of the credential. Use `id` field instead. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + deactivationTime: + description: Date and time when the credential is deactivated. + type: + - string + - 'null' + format: date-time + readOnly: true + type: + description: Type of credential. + type: string + enum: + - webhook + host: + type: string + description: Name of the host. + auth: + $ref: '#/components/schemas/WebhookAuthorization' + required: + - host + - type + ExperianCredential: + type: object + description: >- + Experian integration provides identity verification. + + For more information, see [Experian + integration](https://www.rebilly.com/docs/automations/experian-integration/#experian-integration). + required: + - username + - password + - hmacKey + - publicKey + - type + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + hash: + deprecated: true + type: string + description: ID of the credential. Use `id` field instead. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + deactivationTime: + description: Date and time when the credential is deactivated. + type: + - string + - 'null' + format: date-time + readOnly: true + type: + description: Type of credential. + type: string + readOnly: true + enum: + - experian + username: + type: string + description: Username of the Experian account. + password: + type: string + description: Password of the Experian account. + format: password + hmacKey: + type: string + description: >- + HMAC key that is used to generate the signature for Experian + requests. + format: password + example: PxOclToLSxW4oAB5N5HSdfoslyEWuvfkLjfHeNG2aZY~ + publicKey: + type: string + description: >- + HMAC public key that is included in the signature for Experian + requests. + example: 8831AA5552FE10CD + _links: + $ref: '#/components/schemas/SelfLink' + MailgunCredential: + type: object + description: Mailgun credential. + required: + - emailFrom + - apiKey + - domain + - type + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + hash: + deprecated: true + type: string + description: ID of the credential. Use `id` field instead. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + deactivationTime: + description: Date and time when the credential is deactivated. + type: + - string + - 'null' + format: date-time + readOnly: true + type: + description: Type of credential. + type: string + readOnly: true + enum: + - mailgun + emailFrom: + type: string + format: email + description: Address from which emails are sent. + apiKey: + type: string + description: Mailgun API key. + domain: + type: string + description: Mailgun domain. + _links: + $ref: '#/components/schemas/SelfLink' + OAuth2Credential: + type: object + description: OAuth2 credential. + required: + - code + - scopes + - service + - type + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + hash: + deprecated: true + type: string + description: ID of the credential. Use `id` field instead. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + deactivationTime: + description: Date and time when the credential is deactivated. + type: + - string + - 'null' + format: date-time + readOnly: true + type: + description: Type of credential. + type: string + readOnly: true + enum: + - oauth2 + service: + description: Name of the service to which the OAuth2 credential relates. + type: string + enum: + - google-sheets + - keap-infusionsoft + - intuit-quickbooks + code: + description: OAuth2 code provided by the authentication server. + type: string + example: 2/smAHUUr9jOxw_IOp47Y_dH1r2Y + accessToken: + description: OAuth2 access token. + readOnly: true + type: string + example: >- + zw34.PltIPtJZHmEgZS9R4RoGpzaRrJd5MYjZIONQ2MjWSCj7N7Iqp9BXXFIbkhDRfAPs6cB1pKtTjLUgb3ofzgHUprJfnRiMDTnB_yPMK7vtgobCX4SUs7fhrR6bdApq + refreshToken: + description: OAuth2 refresh token. + readOnly: true + type: string + example: 2/4DPERp2EiySF6JUIOTS4jM5f0JmLG2gPcpGaWYTGU94 + scopes: + description: List of URLs that are granted OAuth2 access. + type: array + items: + type: string + example: + - 'https://www.googleapis.com/auth/drive.metadata.readonly' + _links: + $ref: '#/components/schemas/SelfLink' + PostmarkCredential: + type: object + description: Postmark credential. + required: + - serverApiToken + - type + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + hash: + deprecated: true + type: string + description: ID of the credential. Use `id` field instead. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + deactivationTime: + description: Date and time when the credential is deactivated. + type: + - string + - 'null' + format: date-time + readOnly: true + type: + description: Type of credential. + type: string + readOnly: true + enum: + - postmark + serverApiToken: + type: string + description: Postmark server API token. + example: dd0508z9-2291-6794-3376-z0a70g12eqm9 + _links: + $ref: '#/components/schemas/SelfLink' + SendGridCredential: + type: object + description: SendGrid credential. + required: + - apiKey + - type + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + hash: + deprecated: true + type: string + description: ID of the credential. Use `id` field instead. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + deactivationTime: + description: Date and time when the credential is deactivated. + type: + - string + - 'null' + format: date-time + readOnly: true + type: + description: Type of credential. + type: string + readOnly: true + enum: + - sendgrid + apiKey: + type: string + description: SendGrid API key. + example: >- + SO.WFbRlSWUQJSb40eny4RuZQ.7liHLZ4l1jaPCgbu02b-aGH-bo4RB8z9fK3aUd1heeL + _links: + $ref: '#/components/schemas/SelfLink' + SESCredential: + type: object + description: Amazon Web Services (AWS) Simple Email Service (SES) credential. + required: + - key + - secret + - region + - type + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + hash: + deprecated: true + type: string + description: ID of the credential. Use `id` field instead. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + deactivationTime: + description: Date and time when the credential is deactivated. + type: + - string + - 'null' + format: date-time + readOnly: true + type: + description: Type of credential. + type: string + readOnly: true + enum: + - aws-ses + key: + type: string + description: ID of the AWS access key. + example: BWITYO4UARGDLMFY6UDP + secret: + type: string + description: Secret AWS access key. + example: 8D34yYHOK9+yM7pDnNUO3UTO/5b8Wy/PGNyzTRmG + region: + type: string + description: AWS region name. + example: us-west-2 + configurationSetName: + type: string + description: Name of configuration set from which emails are sent. + example: SpecialConfigurationSet + _links: + $ref: '#/components/schemas/SelfLink' + TaxJarCredential: + type: object + description: >- + TaxJar integration enables you to calculate taxes and create + transactions for the Sales Tax AutoFile feature in your TaxJar account. + + For more information, see [TaxJar + integration](https://www.rebilly.com/docs/automations/tax-jar-integration/#taxjar-integration). + required: + - apiToken + - type + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + hash: + deprecated: true + type: string + description: ID of the credential. Use `id` field instead. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + deactivationTime: + description: Date and time when the credential is deactivated. + type: + - string + - 'null' + format: date-time + readOnly: true + type: + description: Type of credential. + type: string + readOnly: true + enum: + - taxjar + apiToken: + type: string + description: TaxJar API token. + format: password + example: 0ab15e3c1cc608b6f592fbddaddeb8f2 + omitFromAddress: + type: boolean + description: >- + Specifies if `from_` address fields must be omitted in TaxJar + request. + + For more information, see [TaxJar API + guide](https://developers.taxjar.com/api/guides/#when-to-use-from_-address-information). + default: false + example: false + _links: + $ref: '#/components/schemas/SelfLink' + AvalaraCredential: + type: object + description: >- + Avalara integration enables you to calculate taxes and create + transactions in your Avalara account. + required: + - licenseKey + - accountId + - type + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + hash: + deprecated: true + type: string + description: ID of the credential. Use `id` field instead. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + deactivationTime: + description: Date and time when the credential is deactivated. + type: + - string + - 'null' + format: date-time + readOnly: true + type: + description: Type of credential. + type: string + readOnly: true + enum: + - avalara + licenseKey: + type: string + description: Avalara license key. + format: password + example: 0ab15e3c1cc608b6f592fbddaddeb8f2 + accountId: + type: string + description: ID of the Avalara account. + example: a1b2c3d4e5f6 + _links: + $ref: '#/components/schemas/SelfLink' + PlaidCredential: + type: object + description: >- + Plaid integration provides customer bank credential verification, + + and simplifies the process of providing credentials for your customers. + + For more information, see [Plaid + integration](https://www.rebilly.com/docs/automations/plaid-integration/). + required: + - clientId + - secretToken + - type + properties: + id: + type: string + description: ID of the credential. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + hash: + deprecated: true + type: string + description: ID of the credential. Use `id` field instead. + readOnly: true + maxLength: 36 + example: crd_0YV9Y706QGCB39FQD380G1ZHZH + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + deactivationTime: + description: Date and time when the credential is deactivated. + type: + - string + - 'null' + format: date-time + readOnly: true + type: + description: Type of credential. + type: string + readOnly: true + enum: + - plaid + websiteId: + type: string + description: |- + ID of the website that is assigned to the Plaid account. + If this value is not set, credentials are for all websites. + maxLength: 50 + example: web_0YV7DE4Z26DQSA1AC92FBJ7SEG + clientId: + type: string + description: ID of the Plaid client. + example: 5efc712byyf502zz11f9bexx + secretToken: + type: string + description: Plaid secret token. + format: password + example: d184cfdgdb4597dfsa9365f59356b + useStripe: + type: boolean + description: |- + Specifies if a Stripe integration is enabled for the Plaid account. + This setting enables the creation of Stripe bank account tokens. + default: false + example: true + _links: + $ref: '#/components/schemas/SelfLink' + ServiceCredential: + discriminator: + propertyName: type + mapping: + smtp: '#/components/schemas/SmtpCredential' + webhook: '#/components/schemas/WebhookCredential' + experian: '#/components/schemas/ExperianCredential' + mailgun: '#/components/schemas/MailgunCredential' + oauth2: '#/components/schemas/OAuth2Credential' + postmark: '#/components/schemas/PostmarkCredential' + sendgrid: '#/components/schemas/SendGridCredential' + aws-ses: '#/components/schemas/SESCredential' + taxjar: '#/components/schemas/TaxJarCredential' + avalara: '#/components/schemas/AvalaraCredential' + plaid: '#/components/schemas/PlaidCredential' + anyOf: + - $ref: '#/components/schemas/SmtpCredential' + - $ref: '#/components/schemas/WebhookCredential' + - $ref: '#/components/schemas/ExperianCredential' + - $ref: '#/components/schemas/MailgunCredential' + - $ref: '#/components/schemas/OAuth2Credential' + - $ref: '#/components/schemas/PostmarkCredential' + - $ref: '#/components/schemas/SendGridCredential' + - $ref: '#/components/schemas/SESCredential' + - $ref: '#/components/schemas/TaxJarCredential' + - $ref: '#/components/schemas/AvalaraCredential' + - $ref: '#/components/schemas/PlaidCredential' + PatchServiceCredential: + type: object + required: + - status + properties: + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + PatchPlaidCredential: + type: object + description: Plaid credential. + properties: + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + websiteId: + type: string + description: |- + ID of the website that is assigned to the Plaid account. + If this value is not set, credentials are for all websites. + maxLength: 50 + example: web_0YV7DE4Z26DQSA1AC92FBJ7SEG + clientId: + type: string + description: ID of the Plaid client. + example: 5efc712byyf502zz11f9bexx + secretToken: + type: string + description: Plaid secret token. + format: password + example: d184cfdgdb4597dfsa9365f59356b + useStripe: + type: boolean + description: |- + Specifies if a Stripe integration is enabled for the Plaid account. + This setting enables the creation of Stripe bank account tokens. + default: false + example: true + PatchTaxJarCredential: + type: object + description: TaxJar credential. + required: + - status + properties: + status: + description: Status of the credential. + type: string + enum: + - active + - inactive + - deactivated + x-enumDescriptions: + active: Credential is active and can be used. + inactive: >- + Credential is temporarily inactivated and cannot be used until + reactivated. + deactivated: Credential is permanently deactivated and cannot be reactivated. + omitFromAddress: + type: boolean + description: >- + Specifies if `from_` address fields must be omitted in TaxJar + request. + + For more information, see [TaxJar API + guide](https://developers.taxjar.com/api/guides/#when-to-use-from_-address-information). + example: false + GoogleSpreadsheet: + type: object + properties: + id: + description: ID of the Google Spreadsheet. + readOnly: true + type: string + example: 2ytkMntAC2Ke7aIgpaOBjz9IORRlNRjwFqO7KvyNam3B + name: + readOnly: true + description: Name of the Google Spreadsheet. + type: string + example: Spreadsheet 1 + CustomDomain: + type: object + required: + - domain + properties: + id: + description: ID of the custom domain. + type: string + maxLength: 255 + readOnly: true + domain: + description: Fully qualified domain name. + type: string + maxLength: 255 + isVerified: + description: True if the domain is verified by Rebilly. + type: boolean + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + EmailDeliverySetting: + type: object + required: + - from + - name + properties: + id: + type: string + description: ID of the email delivery setting. + readOnly: true + maxLength: 50 + example: mail_dst_0YVCNQV8PSCERAJ7N1V4WWPZN2 + from: + description: >- + Email address of the sender. + + + > **Important:** This email address must be unique within your + account. + type: string + format: email + maxLength: 254 + example: from-example@rebilly.com + name: + description: Name of the sender. + type: string + example: John Doe + status: + description: Status of the email message. + readOnly: true + type: string + enum: + - pending + - verified + credentialId: + type: + - string + - 'null' + description: ID of the SMTP or Email Service Provider (ESP) credential. + example: b120c2ca-6c2b-4690-9dff-3b0d87852dc7 + provider: + readOnly: true + description: Email service provider name. + type: string + enum: + - rebilly + - smtp + - aws-ses + - mailgun + - postmark + - sendgrid + example: rebilly + isDefault: + type: boolean + description: Specifies if the email delivery setting is used by default. + default: false + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + EmailMessage: + type: object + required: + - from + - to + - subject + - text + - html + properties: + id: + type: string + description: ID of the email message. + readOnly: true + maxLength: 50 + example: mail_msg_0YVCNR8ZRBCVMAX28DP4XRFZVA + status: + description: Status of the email message. + type: string + enum: + - draft + - outbox + - sending + - sent + - failed + default: draft + metadata: + description: Metadata associated with the email message. + type: + - object + - 'null' + additionalProperties: + type: string + example: + eventType: subscription-canceled + credentialHash: + type: + - string + - 'null' + description: ID of the SMTP or Email Service Provider (ESP) credential. + example: b120c2ca-6c2b-4690-9dff-3b0d87852dc7 + from: + description: Email address of the sender. + type: string + format: email + maxLength: 254 + example: from-example@rebilly.com + to: + description: List of email addresses to which the email message is sent. + type: array + minItems: 1 + items: + type: string + format: email + maxLength: 254 + example: + - to-example@rebilly.com + cc: + description: List of CC email addresses to which the email message is sent. + type: array + items: + type: string + format: email + maxLength: 254 + example: + - cc-example@rebilly.com + bcc: + description: List of BCC email addresses to which the email message is sent. + type: array + items: + type: string + format: email + maxLength: 254 + example: + - cc-example@rebilly.com + subject: + description: Subject of the email message. + type: string + maxLength: 998 + example: Welcome dear customer! + text: + description: Body of the email message in plain text. + type: string + example: Email message text body + html: + description: Body of the email message in HTML format. + type: string + example:

Email message html body

+ attachments: + description: Attachments of the email message. + type: array + items: + type: object + required: + - resourceType + - resourceId + properties: + resourceType: + description: Type of attachment resource. + type: string + example: invoice + resourceId: + type: string + description: ID of the attachment resource. + maxLength: 50 + example: att_0YV7J787J0DW0918MQQMDHWA7M + responseCode: + type: + - integer + - 'null' + description: >- + Status code returned by a mail service after an attempt to send + email. + readOnly: true + example: 250 + responseBody: + type: + - string + - 'null' + description: >- + Body of response returned by a mail service after an attempt to send + email. + readOnly: true + initiatedTime: + type: + - string + - 'null' + description: Date and time when the email is initiated. + format: date-time + readOnly: true + sentTime: + type: + - string + - 'null' + description: Date and time when the email is sent. + format: date-time + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + EventType: + type: string + description: Type of Rebilly event. + readOnly: true + enum: + - account-password-reset-requested + - account-verification-requested + - aml-list-possibly-matched + - application-instance-disabled + - application-instance-enabled + - balance-transaction-settled + - coupon-application-removed + - coupon-applied + - coupon-expiration-modified + - coupon-expired + - coupon-issued + - coupon-modified + - coupon-redeemed + - coupon-redemption-canceled + - customer-created + - customer-one-time-password-requested + - customer-updated + - dispute-created + - dispute-modified + - experian-check-performed + - gateway-account-downtime-ended + - gateway-account-downtime-started + - gateway-account-limit-reached + - gateway-account-onboarding-completed + - gateway-account-onboarding-failed + - gateway-account-requested + - hard-usage-limit-reached.yaml + - invoice-issued + - invoice-modified + - invoice-paid + - invoice-partially-paid + - invoice-partially-refunded + - invoice-past-due + - invoice-past-due-reminder + - invoice-refunded + - invoice-revenue-recognized + - invoice-tax-calculation-failed + - invoice-voided + - kyc-document-accepted + - kyc-document-modified + - kyc-document-rejected + - kyc-request-fulfilled + - nsf-response-received + - order-abandon-reminder + - order-abandoned + - order-completed + - payment-card-created + - payment-card-expiration-reminder + - payment-card-expired + - payment-instrument-modified + - payout-request-created + - payout-request-modified + - ready-to-pay-requested + - renewal-invoice-issued + - renewal-invoice-payment-canceled + - renewal-invoice-payment-declined + - risk-score-changed + - soft-usage-limit-reached.yaml + - subscription-activated + - subscription-canceled + - subscription-churned + - subscription-created + - subscription-downgraded + - subscription-modified + - subscription-pause-created + - subscription-pause-modified + - subscription-pause-revoked + - subscription-paused + - subscription-reactivated + - subscription-renewal-reminder + - subscription-renewed + - subscription-resumed + - subscription-trial-converted + - subscription-trial-end-changed + - subscription-trial-end-reminder + - subscription-upgraded + - transaction-amount-discrepancy-found + - transaction-declined + - transaction-discrepancy-found + - transaction-process-requested + - transaction-processed + EmailNotification: + type: object + description: Email notification event. + readOnly: true + properties: + eventType: + $ref: '#/components/schemas/EventType' + count: + type: integer + readOnly: true + description: Total number of binds with `send-email` actions per event. + notifications: + readOnly: true + type: array + description: List of notifications. + items: + type: object + properties: + labels: + description: Labels of the notification. + type: array + items: + type: string + title: + type: string + description: Title of the notification. + _links: + $ref: '#/components/schemas/SelfLink' + SystemEvent: + type: object + description: Application event. + readOnly: true + properties: + eventType: + $ref: '#/components/schemas/EventType' + title: + description: Title of the system event. + type: string + description: + description: Description of the system event. + type: string + category: + description: Category of system event. + type: string + enum: + - billing + - payments + rulesCount: + description: >- + Total number of rules associated with the system event. + + A rule is a configuration of an event and one or more actions. + + A rule can be configured to stop subsequent rules in the event list + from being executed. + type: integer + readOnly: true + bindsCount: + description: |- + Total number of binds associated with the system event. + A bind is a configuration of an event and one or more actions. + type: integer + readOnly: true + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - history + - rules + RuleAction: + type: object + discriminator: + propertyName: name + mapping: + add-risk-score: '#/components/schemas/RuleActionAddRiskScore' + adjust-ready-to-pay: '#/components/schemas/RuleActionAdjustReadyToPay' + adjust-ready-to-payout: '#/components/schemas/RuleActionAdjustReadyToPayout' + blocklist: '#/components/schemas/RuleActionAddBlockList' + cancel-scheduled-payments: '#/components/schemas/RuleActionCancelScheduledPayments' + create-intuit-quickbooks-balance-transaction-entry: >- + #/components/schemas/RuleActionCreateIntuitQuickbooksBalanceTransactionEntry + create-intuit-quickbooks-invoice: '#/components/schemas/RuleActionCreateIntuitQuickbooksInvoice' + create-intuit-quickbooks-payment: '#/components/schemas/RuleActionCreateIntuitQuickbooksPayment' + create-intuit-quickbooks-refund-receipt: '#/components/schemas/RuleActionCreateIntuitQuickbooksRefundReceipt' + create-intuit-quickbooks-revenue-recognition-entry: >- + #/components/schemas/RuleActionCreateIntuitQuickbooksRevenueRecognitionEntry + create-keap-infusionsoft-order: '#/components/schemas/RuleActionCreateInfusionsoftOrder' + create-keap-infusionsoft-payment: '#/components/schemas/RuleActionCreateInfusionsoftPayment' + decline-transaction: '#/components/schemas/RuleActionDeclineTransaction' + check-ontario-restriction: '#/components/schemas/RuleActionCheckOntarioRestriction' + display-message: '#/components/schemas/RuleActionDisplayMessage' + display-other-choices: '#/components/schemas/RuleActionDisplayOtherChoices' + guess-payment-card-expiration: '#/components/schemas/RuleActionGuessPaymentCardExpiration' + offer-purchase-bump: '#/components/schemas/RuleActionOfferPurchaseBump' + perform-experian-check: '#/components/schemas/RuleActionPerformExperianCheck' + pick-gateway-account: '#/components/schemas/RuleActionPickGatewayAccount' + remove-reminder: '#/components/schemas/RuleActionRemoveReminder' + request-kyc: '#/components/schemas/RuleActionRequestKyc' + reset-reminder: '#/components/schemas/RuleActionResetReminder' + schedule-invoice-retry: '#/components/schemas/RuleActionScheduleInvoiceRetry' + schedule-payment: '#/components/schemas/RuleActionSchedulePayment' + schedule-reminder: '#/components/schemas/RuleActionScheduleReminder' + send-email: '#/components/schemas/RuleActionSendEmail' + stop-subscriptions: '#/components/schemas/RuleActionStopSubscriptions' + tag-or-untag-customer: '#/components/schemas/RuleActionTagOrUntagCustomer' + update-intuit-quickbooks-invoice: '#/components/schemas/RuleActionUpdateIntuitQuickbooksInvoice' + void-intuit-quickbooks-invoice: '#/components/schemas/RuleActionVoidIntuitQuickbooksInvoice' + properties: + name: + type: string + description: Name of the action. + enum: + - add-risk-score + - adjust-ready-to-pay + - blocklist + - cancel-scheduled-payments + - create-intuit-quickbooks-balance-transaction-entry + - create-intuit-quickbooks-invoice + - create-intuit-quickbooks-payment + - create-intuit-quickbooks-refund-receipt + - create-intuit-quickbooks-revenue-recognition-entry + - create-keap-infusionsoft-order + - create-keap-infusionsoft-payment + - decline-transaction + - check-ontario-restriction + - display-message + - display-other-choices + - guess-payment-card-expiration + - offer-purchase-bump + - perform-experian-check + - pick-gateway-account + - remove-reminder + - request-kyc + - reset-reminder + - schedule-invoice-retry + - schedule-payment + - schedule-reminder + - send-email + - stop-subscriptions + - tag-or-untag-customer + - update-intuit-quickbooks-invoice + - void-intuit-quickbooks-invoice + status: + description: Status of the action. + type: string + default: active + enum: + - active + - inactive + required: + - name + RuleActionAddRiskScore: + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + description: Add risk score. + properties: + score: + type: integer + default: 0 + AdjustReadyToPayPaymentCard: + type: object + title: Payment card + properties: + paymentMethod: + type: string + enum: + - payment-card + feature: + type: + - string + - 'null' + description: Name of the feature. + enum: + - Apple Pay + - Google Pay + AdjustReadyToPayAch: + type: object + title: ACH + properties: + paymentMethod: + type: string + enum: + - ach + feature: + type: + - string + - 'null' + description: Feature name. + enum: + - Plaid + AdjustReadyToPayPaypal: + type: object + title: PayPal + properties: + paymentMethod: + type: string + enum: + - paypal + feature: + type: + - string + - 'null' + description: Feature name. + enum: + - PayPal billing agreement + AdjustReadyToPayGeneric: + type: object + title: Generic + properties: + paymentMethod: + description: Payment method of the payment instrument. + allOf: + - $ref: '#/components/schemas/AlternativePaymentMethods' + - not: + enum: + - paypal + feature: + type: + - string + - 'null' + AdjustPaymentMethod: + type: object + required: + - paymentMethod + - feature + discriminator: + propertyName: paymentMethod + mapping: + payment-card: '#/components/schemas/AdjustReadyToPayPaymentCard' + ach: '#/components/schemas/AdjustReadyToPayAch' + paypal: '#/components/schemas/AdjustReadyToPayPaypal' + cash: '#/components/schemas/AdjustReadyToPayGeneric' + check: '#/components/schemas/AdjustReadyToPayGeneric' + AdvCash: '#/components/schemas/AdjustReadyToPayGeneric' + Alfa-click: '#/components/schemas/AdjustReadyToPayGeneric' + Alipay: '#/components/schemas/AdjustReadyToPayGeneric' + AstroPay Card: '#/components/schemas/AdjustReadyToPayGeneric' + AstroPay-GO: '#/components/schemas/AdjustReadyToPayGeneric' + BankReferenced: '#/components/schemas/AdjustReadyToPayGeneric' + bank-transfer: '#/components/schemas/AdjustReadyToPayGeneric' + bank-transfer-2: '#/components/schemas/AdjustReadyToPayGeneric' + bank-transfer-3: '#/components/schemas/AdjustReadyToPayGeneric' + bank-transfer-4: '#/components/schemas/AdjustReadyToPayGeneric' + bank-transfer-5: '#/components/schemas/AdjustReadyToPayGeneric' + bank-transfer-6: '#/components/schemas/AdjustReadyToPayGeneric' + bank-transfer-7: '#/components/schemas/AdjustReadyToPayGeneric' + bank-transfer-8: '#/components/schemas/AdjustReadyToPayGeneric' + bank-transfer-9: '#/components/schemas/AdjustReadyToPayGeneric' + Baloto: '#/components/schemas/AdjustReadyToPayGeneric' + Beeline: '#/components/schemas/AdjustReadyToPayGeneric' + Belfius-direct-net: '#/components/schemas/AdjustReadyToPayGeneric' + bitcoin: '#/components/schemas/AdjustReadyToPayGeneric' + Bizum: '#/components/schemas/AdjustReadyToPayGeneric' + Boleto: '#/components/schemas/AdjustReadyToPayGeneric' + cash-deposit: '#/components/schemas/AdjustReadyToPayGeneric' + CASHlib: '#/components/schemas/AdjustReadyToPayGeneric' + CashToCode: '#/components/schemas/AdjustReadyToPayGeneric' + China UnionPay: '#/components/schemas/AdjustReadyToPayGeneric' + Cleo: '#/components/schemas/AdjustReadyToPayGeneric' + CODVoucher: '#/components/schemas/AdjustReadyToPayGeneric' + Conekta-oxxo: '#/components/schemas/AdjustReadyToPayGeneric' + Cupon-de-pagos: '#/components/schemas/AdjustReadyToPayGeneric' + cryptocurrency: '#/components/schemas/AdjustReadyToPayGeneric' + domestic-cards: '#/components/schemas/AdjustReadyToPayGeneric' + echeck: '#/components/schemas/AdjustReadyToPayGeneric' + ecoPayz: '#/components/schemas/AdjustReadyToPayGeneric' + ecoVoucher: '#/components/schemas/AdjustReadyToPayGeneric' + Efecty: '#/components/schemas/AdjustReadyToPayGeneric' + EPS: '#/components/schemas/AdjustReadyToPayGeneric' + ePay.bg: '#/components/schemas/AdjustReadyToPayGeneric' + eZeeWallet: '#/components/schemas/AdjustReadyToPayGeneric' + FasterPay: '#/components/schemas/AdjustReadyToPayGeneric' + Flexepin: '#/components/schemas/AdjustReadyToPayGeneric' + Giropay: '#/components/schemas/AdjustReadyToPayGeneric' + Gpaysafe: '#/components/schemas/AdjustReadyToPayGeneric' + Google Pay: '#/components/schemas/AdjustReadyToPayGeneric' + iDebit: '#/components/schemas/AdjustReadyToPayGeneric' + iDEAL: '#/components/schemas/AdjustReadyToPayGeneric' + ING-homepay: '#/components/schemas/AdjustReadyToPayGeneric' + INOVAPAY-pin: '#/components/schemas/AdjustReadyToPayGeneric' + INOVAPAY-wallet: '#/components/schemas/AdjustReadyToPayGeneric' + InstaDebit: '#/components/schemas/AdjustReadyToPayGeneric' + instant-bank-transfer: '#/components/schemas/AdjustReadyToPayGeneric' + InstantPayments: '#/components/schemas/AdjustReadyToPayGeneric' + Interac: '#/components/schemas/AdjustReadyToPayGeneric' + Interac-online: '#/components/schemas/AdjustReadyToPayGeneric' + Interac-eTransfer: '#/components/schemas/AdjustReadyToPayGeneric' + invoice: '#/components/schemas/AdjustReadyToPayGeneric' + iWallet: '#/components/schemas/AdjustReadyToPayGeneric' + Jeton: '#/components/schemas/AdjustReadyToPayGeneric' + jpay: '#/components/schemas/AdjustReadyToPayGeneric' + Khelocard: '#/components/schemas/AdjustReadyToPayGeneric' + Klarna: '#/components/schemas/AdjustReadyToPayGeneric' + KNOT: '#/components/schemas/AdjustReadyToPayGeneric' + loonie: '#/components/schemas/AdjustReadyToPayGeneric' + Matrix: '#/components/schemas/AdjustReadyToPayGeneric' + MaxiCash: '#/components/schemas/AdjustReadyToPayGeneric' + Megafon: '#/components/schemas/AdjustReadyToPayGeneric' + MiFinity-eWallet: '#/components/schemas/AdjustReadyToPayGeneric' + miscellaneous: '#/components/schemas/AdjustReadyToPayGeneric' + Bancontact: '#/components/schemas/AdjustReadyToPayGeneric' + Bancontact-mobile: '#/components/schemas/AdjustReadyToPayGeneric' + MTS: '#/components/schemas/AdjustReadyToPayGeneric' + MuchBetter: '#/components/schemas/AdjustReadyToPayGeneric' + Multibanco: '#/components/schemas/AdjustReadyToPayGeneric' + Neosurf: '#/components/schemas/AdjustReadyToPayGeneric' + Netbanking: '#/components/schemas/AdjustReadyToPayGeneric' + Neteller: '#/components/schemas/AdjustReadyToPayGeneric' + Nordea-Solo: '#/components/schemas/AdjustReadyToPayGeneric' + OchaPay: '#/components/schemas/AdjustReadyToPayGeneric' + online-bank-transfer: '#/components/schemas/AdjustReadyToPayGeneric' + Onlineueberweisen: '#/components/schemas/AdjustReadyToPayGeneric' + oriental-wallet: '#/components/schemas/AdjustReadyToPayGeneric' + OXXO: '#/components/schemas/AdjustReadyToPayGeneric' + P24: '#/components/schemas/AdjustReadyToPayGeneric' + Pagadito: '#/components/schemas/AdjustReadyToPayGeneric' + PagoEffectivo: '#/components/schemas/AdjustReadyToPayGeneric' + Pagsmile-deposit-express: '#/components/schemas/AdjustReadyToPayGeneric' + Pagsmile-lottery: '#/components/schemas/AdjustReadyToPayGeneric' + PayCash: '#/components/schemas/AdjustReadyToPayGeneric' + Payeer: '#/components/schemas/AdjustReadyToPayGeneric' + PaymentAsia-crypto: '#/components/schemas/AdjustReadyToPayGeneric' + Paymero: '#/components/schemas/AdjustReadyToPayGeneric' + Perfect-money: '#/components/schemas/AdjustReadyToPayGeneric' + Piastrix: '#/components/schemas/AdjustReadyToPayGeneric' + plaid-account: '#/components/schemas/AdjustReadyToPayGeneric' + PayTabs: '#/components/schemas/AdjustReadyToPayGeneric' + Paysafecard: '#/components/schemas/AdjustReadyToPayGeneric' + Paysafecash: '#/components/schemas/AdjustReadyToPayGeneric' + Pay4Fun: '#/components/schemas/AdjustReadyToPayGeneric' + Paynote: '#/components/schemas/AdjustReadyToPayGeneric' + PinPay: '#/components/schemas/AdjustReadyToPayGeneric' + phone: '#/components/schemas/AdjustReadyToPayGeneric' + PhonePe: '#/components/schemas/AdjustReadyToPayGeneric' + POLi: '#/components/schemas/AdjustReadyToPayGeneric' + PostFinance-card: '#/components/schemas/AdjustReadyToPayGeneric' + PostFinance-e-finance: '#/components/schemas/AdjustReadyToPayGeneric' + QIWI: '#/components/schemas/AdjustReadyToPayGeneric' + QPay: '#/components/schemas/AdjustReadyToPayGeneric' + QQPay: '#/components/schemas/AdjustReadyToPayGeneric' + rapyd-checkout: '#/components/schemas/AdjustReadyToPayGeneric' + Resurs: '#/components/schemas/AdjustReadyToPayGeneric' + SafetyPay: '#/components/schemas/AdjustReadyToPayGeneric' + SEPA: '#/components/schemas/AdjustReadyToPayGeneric' + Skrill: '#/components/schemas/AdjustReadyToPayGeneric' + Skrill Rapid Transfer: '#/components/schemas/AdjustReadyToPayGeneric' + SMSVoucher: '#/components/schemas/AdjustReadyToPayGeneric' + Sofort: '#/components/schemas/AdjustReadyToPayGeneric' + SparkPay: '#/components/schemas/AdjustReadyToPayGeneric' + swift-dbt: '#/components/schemas/AdjustReadyToPayGeneric' + Tele2: '#/components/schemas/AdjustReadyToPayGeneric' + Terminaly-RF: '#/components/schemas/AdjustReadyToPayGeneric' + ToditoCash-card: '#/components/schemas/AdjustReadyToPayGeneric' + Trustly: '#/components/schemas/AdjustReadyToPayGeneric' + UPayCard: '#/components/schemas/AdjustReadyToPayGeneric' + UPI: '#/components/schemas/AdjustReadyToPayGeneric' + USD-coin: '#/components/schemas/AdjustReadyToPayGeneric' + VCreditos: '#/components/schemas/AdjustReadyToPayGeneric' + VenusPoint: '#/components/schemas/AdjustReadyToPayGeneric' + voucher: '#/components/schemas/AdjustReadyToPayGeneric' + voucher-2: '#/components/schemas/AdjustReadyToPayGeneric' + voucher-3: '#/components/schemas/AdjustReadyToPayGeneric' + voucher-4: '#/components/schemas/AdjustReadyToPayGeneric' + Webmoney: '#/components/schemas/AdjustReadyToPayGeneric' + Webpay: '#/components/schemas/AdjustReadyToPayGeneric' + Webpay-2: '#/components/schemas/AdjustReadyToPayGeneric' + Webpay Card: '#/components/schemas/AdjustReadyToPayGeneric' + WeChat Pay: '#/components/schemas/AdjustReadyToPayGeneric' + XPay-P2P: '#/components/schemas/AdjustReadyToPayGeneric' + XPay-QR: '#/components/schemas/AdjustReadyToPayGeneric' + Yandex-money: '#/components/schemas/AdjustReadyToPayGeneric' + Zotapay: '#/components/schemas/AdjustReadyToPayGeneric' + Zimpler: '#/components/schemas/AdjustReadyToPayGeneric' + anyOf: + - $ref: '#/components/schemas/AdjustReadyToPayPaymentCard' + - $ref: '#/components/schemas/AdjustReadyToPayAch' + - $ref: '#/components/schemas/AdjustReadyToPayPaypal' + - $ref: '#/components/schemas/AdjustReadyToPayGeneric' + RuleActionAdjustReadyToPay: + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + description: Adjust ready to pay. + properties: + prioritizeActivePaymentInstruments: + type: boolean + default: false + description: >- + If set as `true`, the payment methods associated to the + customer's active payment instruments are displayed at the top + of the list. + paymentMethods: + type: array + description: >- + Ordered list of payment methods allowed. If empty, then no + payment methods are allowed. + items: + $ref: '#/components/schemas/AdjustPaymentMethod' + AdjustReadyToPayoutGeneric: + type: object + title: Generic + properties: + paymentMethod: + description: Payment method of the payment instrument. + $ref: '#/components/schemas/AlternativePaymentMethods' + AdjustReadyToPayoutPaymentMethod: + type: object + required: + - paymentMethod + - feature + discriminator: + propertyName: paymentMethod + mapping: + payment-card: '#/components/schemas/AdjustReadyToPayoutGeneric' + ach: '#/components/schemas/AdjustReadyToPayoutGeneric' + paypal: '#/components/schemas/AdjustReadyToPayoutGeneric' + cash: '#/components/schemas/AdjustReadyToPayoutGeneric' + check: '#/components/schemas/AdjustReadyToPayoutGeneric' + AdvCash: '#/components/schemas/AdjustReadyToPayoutGeneric' + Alfa-click: '#/components/schemas/AdjustReadyToPayoutGeneric' + Alipay: '#/components/schemas/AdjustReadyToPayoutGeneric' + AstroPay Card: '#/components/schemas/AdjustReadyToPayoutGeneric' + AstroPay-GO: '#/components/schemas/AdjustReadyToPayoutGeneric' + BankReferenced: '#/components/schemas/AdjustReadyToPayoutGeneric' + bank-transfer: '#/components/schemas/AdjustReadyToPayoutGeneric' + bank-transfer-2: '#/components/schemas/AdjustReadyToPayoutGeneric' + bank-transfer-3: '#/components/schemas/AdjustReadyToPayoutGeneric' + bank-transfer-4: '#/components/schemas/AdjustReadyToPayoutGeneric' + bank-transfer-5: '#/components/schemas/AdjustReadyToPayoutGeneric' + bank-transfer-6: '#/components/schemas/AdjustReadyToPayoutGeneric' + bank-transfer-7: '#/components/schemas/AdjustReadyToPayoutGeneric' + bank-transfer-8: '#/components/schemas/AdjustReadyToPayoutGeneric' + bank-transfer-9: '#/components/schemas/AdjustReadyToPayoutGeneric' + Baloto: '#/components/schemas/AdjustReadyToPayoutGeneric' + Beeline: '#/components/schemas/AdjustReadyToPayoutGeneric' + Belfius-direct-net: '#/components/schemas/AdjustReadyToPayoutGeneric' + bitcoin: '#/components/schemas/AdjustReadyToPayoutGeneric' + Bizum: '#/components/schemas/AdjustReadyToPayoutGeneric' + Boleto: '#/components/schemas/AdjustReadyToPayoutGeneric' + cash-deposit: '#/components/schemas/AdjustReadyToPayoutGeneric' + CASHlib: '#/components/schemas/AdjustReadyToPayoutGeneric' + CashToCode: '#/components/schemas/AdjustReadyToPayoutGeneric' + China UnionPay: '#/components/schemas/AdjustReadyToPayoutGeneric' + Cleo: '#/components/schemas/AdjustReadyToPayoutGeneric' + CODVoucher: '#/components/schemas/AdjustReadyToPayoutGeneric' + Conekta-oxxo: '#/components/schemas/AdjustReadyToPayoutGeneric' + Cupon-de-pagos: '#/components/schemas/AdjustReadyToPayoutGeneric' + cryptocurrency: '#/components/schemas/AdjustReadyToPayoutGeneric' + domestic-cards: '#/components/schemas/AdjustReadyToPayoutGeneric' + echeck: '#/components/schemas/AdjustReadyToPayoutGeneric' + ecoPayz: '#/components/schemas/AdjustReadyToPayoutGeneric' + ecoVoucher: '#/components/schemas/AdjustReadyToPayoutGeneric' + Efecty: '#/components/schemas/AdjustReadyToPayoutGeneric' + EPS: '#/components/schemas/AdjustReadyToPayoutGeneric' + ePay.bg: '#/components/schemas/AdjustReadyToPayoutGeneric' + eZeeWallet: '#/components/schemas/AdjustReadyToPayoutGeneric' + FasterPay: '#/components/schemas/AdjustReadyToPayoutGeneric' + Flexepin: '#/components/schemas/AdjustReadyToPayoutGeneric' + Giropay: '#/components/schemas/AdjustReadyToPayoutGeneric' + Gpaysafe: '#/components/schemas/AdjustReadyToPayoutGeneric' + Google Pay: '#/components/schemas/AdjustReadyToPayoutGeneric' + iDebit: '#/components/schemas/AdjustReadyToPayoutGeneric' + iDEAL: '#/components/schemas/AdjustReadyToPayoutGeneric' + ING-homepay: '#/components/schemas/AdjustReadyToPayoutGeneric' + INOVAPAY-pin: '#/components/schemas/AdjustReadyToPayoutGeneric' + INOVAPAY-wallet: '#/components/schemas/AdjustReadyToPayoutGeneric' + InstaDebit: '#/components/schemas/AdjustReadyToPayoutGeneric' + instant-bank-transfer: '#/components/schemas/AdjustReadyToPayoutGeneric' + InstantPayments: '#/components/schemas/AdjustReadyToPayoutGeneric' + Interac: '#/components/schemas/AdjustReadyToPayoutGeneric' + Interac-online: '#/components/schemas/AdjustReadyToPayoutGeneric' + Interac-eTransfer: '#/components/schemas/AdjustReadyToPayoutGeneric' + invoice: '#/components/schemas/AdjustReadyToPayoutGeneric' + iWallet: '#/components/schemas/AdjustReadyToPayoutGeneric' + Jeton: '#/components/schemas/AdjustReadyToPayoutGeneric' + jpay: '#/components/schemas/AdjustReadyToPayoutGeneric' + Khelocard: '#/components/schemas/AdjustReadyToPayoutGeneric' + Klarna: '#/components/schemas/AdjustReadyToPayoutGeneric' + KNOT: '#/components/schemas/AdjustReadyToPayoutGeneric' + loonie: '#/components/schemas/AdjustReadyToPayoutGeneric' + Matrix: '#/components/schemas/AdjustReadyToPayoutGeneric' + MaxiCash: '#/components/schemas/AdjustReadyToPayoutGeneric' + Megafon: '#/components/schemas/AdjustReadyToPayoutGeneric' + MiFinity-eWallet: '#/components/schemas/AdjustReadyToPayoutGeneric' + miscellaneous: '#/components/schemas/AdjustReadyToPayoutGeneric' + Bancontact: '#/components/schemas/AdjustReadyToPayoutGeneric' + Bancontact-mobile: '#/components/schemas/AdjustReadyToPayoutGeneric' + MTS: '#/components/schemas/AdjustReadyToPayoutGeneric' + MuchBetter: '#/components/schemas/AdjustReadyToPayoutGeneric' + Multibanco: '#/components/schemas/AdjustReadyToPayoutGeneric' + Neosurf: '#/components/schemas/AdjustReadyToPayoutGeneric' + Netbanking: '#/components/schemas/AdjustReadyToPayoutGeneric' + Neteller: '#/components/schemas/AdjustReadyToPayoutGeneric' + Nordea-Solo: '#/components/schemas/AdjustReadyToPayoutGeneric' + OchaPay: '#/components/schemas/AdjustReadyToPayoutGeneric' + online-bank-transfer: '#/components/schemas/AdjustReadyToPayoutGeneric' + Onlineueberweisen: '#/components/schemas/AdjustReadyToPayoutGeneric' + oriental-wallet: '#/components/schemas/AdjustReadyToPayoutGeneric' + OXXO: '#/components/schemas/AdjustReadyToPayoutGeneric' + P24: '#/components/schemas/AdjustReadyToPayoutGeneric' + Pagadito: '#/components/schemas/AdjustReadyToPayoutGeneric' + PagoEffectivo: '#/components/schemas/AdjustReadyToPayoutGeneric' + Pagsmile-deposit-express: '#/components/schemas/AdjustReadyToPayoutGeneric' + Pagsmile-lottery: '#/components/schemas/AdjustReadyToPayoutGeneric' + PayCash: '#/components/schemas/AdjustReadyToPayoutGeneric' + Payeer: '#/components/schemas/AdjustReadyToPayoutGeneric' + PaymentAsia-crypto: '#/components/schemas/AdjustReadyToPayoutGeneric' + Paymero: '#/components/schemas/AdjustReadyToPayoutGeneric' + Perfect-money: '#/components/schemas/AdjustReadyToPayoutGeneric' + Piastrix: '#/components/schemas/AdjustReadyToPayoutGeneric' + plaid-account: '#/components/schemas/AdjustReadyToPayoutGeneric' + PayTabs: '#/components/schemas/AdjustReadyToPayoutGeneric' + Paysafecard: '#/components/schemas/AdjustReadyToPayoutGeneric' + Paysafecash: '#/components/schemas/AdjustReadyToPayoutGeneric' + Pay4Fun: '#/components/schemas/AdjustReadyToPayoutGeneric' + Paynote: '#/components/schemas/AdjustReadyToPayoutGeneric' + PinPay: '#/components/schemas/AdjustReadyToPayoutGeneric' + phone: '#/components/schemas/AdjustReadyToPayoutGeneric' + PhonePe: '#/components/schemas/AdjustReadyToPayoutGeneric' + POLi: '#/components/schemas/AdjustReadyToPayoutGeneric' + PostFinance-card: '#/components/schemas/AdjustReadyToPayoutGeneric' + PostFinance-e-finance: '#/components/schemas/AdjustReadyToPayoutGeneric' + QIWI: '#/components/schemas/AdjustReadyToPayoutGeneric' + QPay: '#/components/schemas/AdjustReadyToPayoutGeneric' + QQPay: '#/components/schemas/AdjustReadyToPayoutGeneric' + rapyd-checkout: '#/components/schemas/AdjustReadyToPayoutGeneric' + Resurs: '#/components/schemas/AdjustReadyToPayoutGeneric' + SafetyPay: '#/components/schemas/AdjustReadyToPayoutGeneric' + SEPA: '#/components/schemas/AdjustReadyToPayoutGeneric' + Skrill: '#/components/schemas/AdjustReadyToPayoutGeneric' + Skrill Rapid Transfer: '#/components/schemas/AdjustReadyToPayoutGeneric' + SMSVoucher: '#/components/schemas/AdjustReadyToPayoutGeneric' + Sofort: '#/components/schemas/AdjustReadyToPayoutGeneric' + SparkPay: '#/components/schemas/AdjustReadyToPayoutGeneric' + swift-dbt: '#/components/schemas/AdjustReadyToPayoutGeneric' + Tele2: '#/components/schemas/AdjustReadyToPayoutGeneric' + Terminaly-RF: '#/components/schemas/AdjustReadyToPayoutGeneric' + ToditoCash-card: '#/components/schemas/AdjustReadyToPayoutGeneric' + Trustly: '#/components/schemas/AdjustReadyToPayoutGeneric' + UPayCard: '#/components/schemas/AdjustReadyToPayoutGeneric' + UPI: '#/components/schemas/AdjustReadyToPayoutGeneric' + USD-coin: '#/components/schemas/AdjustReadyToPayoutGeneric' + VCreditos: '#/components/schemas/AdjustReadyToPayoutGeneric' + VenusPoint: '#/components/schemas/AdjustReadyToPayoutGeneric' + voucher: '#/components/schemas/AdjustReadyToPayoutGeneric' + voucher-2: '#/components/schemas/AdjustReadyToPayoutGeneric' + voucher-3: '#/components/schemas/AdjustReadyToPayoutGeneric' + voucher-4: '#/components/schemas/AdjustReadyToPayoutGeneric' + Webmoney: '#/components/schemas/AdjustReadyToPayoutGeneric' + Webpay: '#/components/schemas/AdjustReadyToPayoutGeneric' + Webpay-2: '#/components/schemas/AdjustReadyToPayoutGeneric' + Webpay Card: '#/components/schemas/AdjustReadyToPayoutGeneric' + WeChat Pay: '#/components/schemas/AdjustReadyToPayoutGeneric' + XPay-P2P: '#/components/schemas/AdjustReadyToPayoutGeneric' + XPay-QR: '#/components/schemas/AdjustReadyToPayoutGeneric' + Yandex-money: '#/components/schemas/AdjustReadyToPayoutGeneric' + Zotapay: '#/components/schemas/AdjustReadyToPayoutGeneric' + Zimpler: '#/components/schemas/AdjustReadyToPayoutGeneric' + anyOf: + - $ref: '#/components/schemas/AdjustReadyToPayoutGeneric' + RuleActionAdjustReadyToPayout: + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + description: Adjust ready to payout. + properties: + prioritizeActivePaymentInstruments: + type: boolean + default: false + description: >- + Specifies if payment methods, that are associated with the + customer's active payment instruments, are displayed at the top + of the list. + paymentMethods: + type: array + description: >- + Ordered list of allowed payment methods. If this field is empty, + no payment methods are allowed. + items: + $ref: '#/components/schemas/AdjustReadyToPayoutPaymentMethod' + RuleActionAddBlockList: + description: Add customer data to blocklist. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + type: + type: string + enum: + - customer-id + - email + - fingerprint + - ip-address + - payment-card + ttl: + type: integer + description: >- + Blocklist TTL. Defaults to zero, meaning blocklist record won't + expire ever. + default: 0 + required: + - type + RuleActionCancelScheduledPayments: + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + skipStartedServicePeriod: + type: boolean + description: Skip invoices with started service period. + default: false + RuleActionCreateIntuitQuickbooksBalanceTransactionEntry: + description: Create an Intuit QuickBooks balance transaction entry. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + debitAccount: + description: ID of the debit QuickBooks account. + type: string + creditAccount: + description: ID of the credit QuickBooks account. + type: string + description: + description: >- + QuickBooks balance transaction entry description. + + + For example, `Balance transaction of type {{ + balanceTransaction.type }} for transaction #{{ transaction.id + }}`. + type: string + credentialHash: + type: string + description: ID of the OAuth2 credential. + required: + - credentialHash + - debitAccount + - creditAccount + - description + RuleActionUpdateIntuitQuickbooksInvoice: + description: Update an Intuit QuickBooks invoice. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + unearnedRevenueAccount: + description: ID of the unearned revenue QuickBooks account. + type: string + taxesAccount: + description: >- + ID of the taxes QuickBooks account. + + If supplied taxes are represented as separate line items instead + of integrated tax field. + type: + - string + - 'null' + discountsAccount: + description: |- + ID of the discounts QuickBooks account. + If not set `unearnedRevenueAccount` is used for discounts. + type: + - string + - 'null' + department: + description: ID of the QuickBooks department. + type: + - string + - 'null' + template: + type: object + properties: + itemName: + description: Name of the line item. + type: string + default: >- + {{ item.plan.id | default: item.product.id | default: + item.description }} + itemDescription: + description: Description of the line item. + type: string + default: '{{ item.product.description | default: item.description }}' + itemSku: + description: SKU of the line item. + type: string + default: >- + {{ item.plan.id | default: item.product.id | default: + item.id }} + itemLineDescription: + description: Description of the line item. + type: string + default: >- + {{ item.plan.id | default: item.product.id | default: + item.description }}{% if item.periodStartTime and + item.periodEndTime %} ({{ item.periodStartTime | datetime: + "M jS, Y" }} – {{ item.periodEndTime | datetime: "M jS, Y" + }}){% endif %} + taxName: + description: >- + Name of the tax item name. + + This value is used when the `taxesAccount` field is + configured. + type: string + default: '{{ tax.description }}' + taxDescription: + description: Description of the tax item. + type: string + default: >- + {{ tax.description }} This value is used when the + `taxesAccount` field is configured. + taxSku: + description: SKU of the tax item. + type: string + default: tax + taxLineDescription: + description: >- + Description of the tax line. + + This value is used when the `taxesAccount` field is + configured. + type: string + default: >- + {{ tax.description }}{% if item %} for {{ item.plan.id | + default: item.description }}{% if item.periodStartTime and + item.periodEndTime %} ({{ item.periodStartTime | datetime: + "M jS, Y" }} – {{ item.periodEndTime | datetime: "M jS, Y" + }}){% else %} for multiple items{% endif %}{% endif %} + credentialHash: + type: string + description: ID of the OAuth2 credential. + required: + - credentialHash + - customerDisplayName + - unearnedRevenueAccount + RuleActionCreateIntuitQuickbooksInvoice: + description: Create an Intuit QuickBooks invoice. + allOf: + - $ref: '#/components/schemas/RuleActionUpdateIntuitQuickbooksInvoice' + - type: object + properties: + customerDisplayName: + description: >- + Customer display name in QuickBooks. Duplicate names are mapped + to a single QuickBooks customer. + + + Event placeholders could be used as follows: + + - Organization name: `{{ + invoice.customer.primaryAddress.organization }}`; + + - Full customer name: `{{ invoice.customer.firstName }} {{ + invoice.customer.lastName }}`; + + - Customer ID: `{{ invoice.customer.id }}`. + type: string + required: + - customerDisplayName + RuleActionCreateIntuitQuickbooksPayment: + description: Create an Intuit QuickBooks payment. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + depositAccount: + description: ID of the deposit QuickBooks account. + type: string + credentialHash: + type: string + description: ID of the OAuth2 credential. + required: + - credentialHash + - depositAccount + RuleActionCreateIntuitQuickbooksRefundReceipt: + description: Create an Intuit QuickBooks refund receipt. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + depositAccount: + description: ID of the deposit QuickBooks account. + type: string + department: + description: ID of the QuickBooks department. + type: + - string + - 'null' + credentialHash: + type: string + description: ID of the OAuth2 credential. + required: + - credentialHash + - depositAccount + RuleActionCreateIntuitQuickbooksRevenueRecognitionEntry: + description: Create an Intuit QuickBooks revenue recognition entry. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + debitAccount: + description: ID of the debit QuickBooks account. + type: string + creditAccount: + description: ID of the credit QuickBooks account. + type: string + description: + description: >- + QuickBooks revenue recognition entry description. + + + For example, `Revenue recognition for invoice #{{ invoice.id }} + item #{{ item.id }} – {{ item.description }}`. + type: string + credentialHash: + type: string + description: ID of the OAuth2 credential. + required: + - credentialHash + - debitAccount + - creditAccount + - description + RuleActionCreateInfusionsoftOrder: + description: Create a Keap Infusionsoft order along with a contact. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + contactBody: + description: >- + List of contact attributes used during Keap Infusionsoft + creation process. + type: object + properties: + email_addresses: + description: >- + Contact email addresses used during Keap Infusionsoft + creation process. + type: array + minItems: 1 + items: + type: object + properties: + email: + description: >- + Contact email address used during Keap Infusionsoft + creation process. + type: string + example: '{{ invoice.customer.email }}' + field: + description: >- + Contact email field type used during Keap Infusionsoft + creation process. + type: string + enum: + - EMAIL1 + - EMAIL2 + - EMAIL3 + example: EMAIL1 + phone_numbers: + description: >- + Contact phone numbers used during Keap Infusionsoft creation + process. + type: array + minItems: 1 + items: + type: object + properties: + number: + description: >- + Contact phone number used during Keap Infusionsoft + creation process. + type: string + example: '{{ invoice.customer.customFields.phoneNumber }}' + field: + description: >- + Contact phone field type used during Keap Infusionsoft + creation process. + type: string + enum: + - PHONE1 + - PHONE2 + - PHONE3 + - PHONE4 + - PHONE5 + example: PHONE1 + orderBody: + description: >- + List of order attributes used during Keap Infusionsoft creation + process except `contact_id`. + type: object + properties: + order_date: + description: Order date used during Keap Infusionsoft creation process. + type: string + example: '{{ invoice.issuedTime }}' + order_title: + description: Order title used during Keap Infusionsoft creation process. + type: string + example: '{{ invoice.id }}' + order_type: + description: >- + Order type that used during Keap Infusionsoft creation + process. + type: string + enum: + - Offline + - Online + example: Offline + required: + - order_date + - order_title + - order_type + credentialHash: + type: string + description: ID of the OAuth2 credential. + required: + - credentialHash + - contactBody + - orderBody + RuleActionCreateInfusionsoftPayment: + description: Create a Keap Infusionsoft payment. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + paymentBody: + description: >- + List of payment attributes used during Keap Infusionsoft + creation process. + type: object + credentialHash: + type: string + description: ID of the OAuth2 credential. + required: + - credentialHash + RuleActionDeclineTransaction: + description: Decline transaction. + allOf: + - $ref: '#/components/schemas/RuleAction' + RuleActionCheckOntarioRestriction: + description: Decline transaction not conforming Ontario's restrictions. + allOf: + - $ref: '#/components/schemas/RuleAction' + RuleActionDisplayMessage: + description: Display message. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + messages: + type: array + minItems: 1 + items: + type: object + properties: + langIso: + description: Language in ISO 639-1 code format. + type: string + content: + type: string + required: + - langIso + - content + required: + - messages + RuleActionDisplayOtherChoices: + description: Display other choices. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + choices: + type: array + minItems: 1 + items: + type: object + properties: + langIso: + description: Language in ISO 639-1 code format. + type: string + content: + type: string + required: + - langIso + - content + required: + - choices + RuleActionGuessPaymentCardExpiration: + allOf: + - $ref: '#/components/schemas/RuleAction' + RuleActionOfferPurchaseBump: + description: Offer purchase bump. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + required: + - bumpOffers + properties: + bumpOffers: + type: array + minItems: 1 + items: + type: object + required: + - name + - weight + - offers + - choices + properties: + name: + type: string + description: |- + Name of the bump offer version. + This field is useful when measuring split tests. + weight: + description: Weight of the bump offer. + type: integer + minimum: 0 + offers: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/PurchaseBumpOffer' + choices: + type: array + minItems: 1 + items: + type: object + required: + - langIso + - content + properties: + langIso: + $ref: '#/components/schemas/LanguageIsoCode' + content: + type: string + RuleActionPerformExperianCheck: + description: Perform Experian ProveID check on the customer. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + experianCredentialHash: + description: Hash of the Experian credential to use for performing the check. + type: string + example: 58816e40-387e-4834-982f-fa1855a75f64 + tagOnApprove: + description: >- + Tag ID that should be used to tag the customer when the identity + matches Experian records. + type: string + tagOnReject: + description: >- + Tag ID that should be used to tag the customer when the identity + doesn't match Experian records. + type: string + tagOnUnknown: + description: >- + Tag ID that should be used to tag the customer when the identity + is not found in Experian records or further analysis needed. + type: string + required: + - experianCredentialHash + GatewayAccountPickInstruction: + type: object + discriminator: + propertyName: method + mapping: + gateway-account-weights: '#/components/schemas/PickInstructionGatewayAccountWeights' + gateway-acquirer-weights: '#/components/schemas/PickInstructionGatewayAcquirerWeights' + properties: + strategy: + type: string + enum: + - weighted-random + - round-robin + description: >- + Controls the picking strategy for gateway accounts. + + The weighted-random strategy picks based on weighted-random every + time. + + The round-robin strategy tries a weighted-random different gateway + account for consecutive attempts from same payment instrument. + default: weighted-random + method: + type: string + enum: + - gateway-account-weights + - gateway-acquirer-weights + required: + - method + PickInstructionGatewayAccountWeights: + allOf: + - $ref: '#/components/schemas/GatewayAccountPickInstruction' + - type: object + properties: + weightedList: + type: array + uniqueItems: true + minimum: 0 + items: + type: object + properties: + gatewayAccountId: + type: string + description: Unique resource ID. + maxLength: 50 + example: gw_acc_0YVCXMF26DDNKAERE5NW727S34 + weight: + type: integer + minimum: 0 + required: + - gatewayAccountId + - weight + example: + - gatewayAccountId: my_gateway_account_1 + weight: 80 + - gatewayAccountId: my_gateway_account_2 + weight: 20 + required: + - weightedList + PickInstructionGatewayAcquirerWeights: + allOf: + - $ref: '#/components/schemas/GatewayAccountPickInstruction' + - type: object + properties: + weightedList: + type: array + uniqueItems: true + minimum: 0 + items: + type: object + properties: + gatewayName: + $ref: '#/components/schemas/GatewayName' + acquirerName: + $ref: '#/components/schemas/AcquirerName' + weight: + type: integer + minimum: 0 + required: + - gatewayName + - acquirerName + - weight + example: + - gatewayName: TestProcessor + acquirerName: AIB + weight: 80 + - gatewayName: TestProcessor + acquirerName: B+S + weight: 20 + required: + - weightedList + RuleActionPickGatewayAccount: + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + pickInstruction: + $ref: '#/components/schemas/GatewayAccountPickInstruction' + required: + - pickInstruction + RuleActionRemoveReminder: + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + role: + type: string + enum: + - all + - renewal + - trial-end + description: >- + Role of Reminder (available only on Order events, other events + should use `all`). + required: + - role + RuleActionRequestKyc: + description: Request KYC page to verify customer identity. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + documents: + type: array + description: Documents to request from the customer. + minItems: 1 + items: + $ref: '#/components/schemas/KycRequestDocument' + excludePolicy: + type: string + description: Specifies who should be excluded from the verification. + enum: + - customers-with-accepted-document + - customers-with-document + - none + default: customers-with-accepted-document + isMandatory: + type: boolean + description: Is the verification mandatory. + default: true + promptPolicy: + type: string + description: 'When to prompt, before or after processing the transaction.' + enum: + - before-transaction-process + - after-transaction-process + default: before-transaction-process + rejectedBeforeTransactionProcessPolicy: + type: string + description: >- + What to do if verification is before transaction processing, and + is rejected. + enum: + - process-transaction + - decline + - use-alternate-gateway + default: decline + alternateGatewayAccountIfRejected: + type: string + description: >- + Gateway account to use if use-alternate-gateway is selected for + rejectedBeforeTransactionProcessPolicy. + rejectedAfterTransactionProcessPolicy: + type: string + description: >- + What to do if verification is after transaction processing, and + is rejected. + enum: + - proceed + default: proceed + optionalPolicy: + type: string + description: What to do if verification is optional. + enum: + - allow-bypass + - allow-use-alternate-gateway + default: allow-bypass + alternateGatewayAccountIfOptional: + type: string + description: >- + Gateway account to use if allow-use-alternate-gateway is + selected for optionalPolicy. + bypassCurrencyToDisplay: + description: >- + Currency three letter code to display on the bypass link, if + optional. + type: string + default: USD + required: + - documents + - excludePolicy + - isMandatory + - promptPolicy + - rejectedBeforeTransactionProcessPolicy + - rejectedAfterTransactionProcessPolicy + - optionalPolicy + RuleActionResetReminder: + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + role: + type: string + enum: + - all + - renewal + - trial-end + description: >- + Role of Reminder (available only on Order events, other events + should use `all`). + required: + - role + RuleActionScheduleInvoiceRetry: + description: Schedule an invoice retry. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + attempts: + type: array + description: Describes the retry instruction. + minItems: 1 + items: + type: object + properties: + scheduleInstruction: + $ref: '#/components/schemas/InvoiceRetryScheduleInstruction' + amountAdjustmentInstruction: + $ref: >- + #/components/schemas/InvoiceRetryAmountAdjustmentInstruction + tryBackupInstruments: + description: >- + Specifies whether to use backup payment instruments on an + invoice payment retry. + type: boolean + default: false + required: + - scheduleInstruction + afterAttemptPolicies: + description: Policy on the attempt finishes. + type: array + items: + type: string + enum: + - change-subscription-renewal-time + afterRetryEndPolicies: + description: Policy on the retry ends. + type: array + items: + type: string + enum: + - abandon-invoice + - cancel-subscription + overrideRetryInstruction: + description: Specifies whether to replace the existing retry. + type: boolean + required: + - attempts + - afterAttemptPolicies + - afterRetryEndPolicies + - overrideRetryInstruction + SchedulingMethodAuto: + type: object + required: + - method + properties: + method: + type: string + enum: + - auto + ScheduleInstruction: + type: object + description: Specifies when the instruction is performed. + discriminator: + propertyName: method + mapping: + auto: '#/components/schemas/SchedulingMethodAuto' + date-interval: '#/components/schemas/SchedulingMethodDateInterval' + day-of-month: '#/components/schemas/SchedulingMethodDayOfMonth' + day-of-week: '#/components/schemas/SchedulingMethodDayOfWeek' + immediately: '#/components/schemas/SchedulingMethodImmediately' + intelligent: '#/components/schemas/SchedulingMethodIntelligent' + anyOf: + - $ref: '#/components/schemas/SchedulingMethodAuto' + - $ref: '#/components/schemas/SchedulingMethodDateInterval' + - $ref: '#/components/schemas/SchedulingMethodDayOfMonth' + - $ref: '#/components/schemas/SchedulingMethodDayOfWeek' + - $ref: '#/components/schemas/SchedulingMethodImmediately' + - $ref: '#/components/schemas/SchedulingMethodIntelligent' + RuleActionSchedulePayment: + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + description: Calculation instruction of scheduled time for payment. + properties: + scheduleInstruction: + $ref: '#/components/schemas/ScheduleInstruction' + amountPolicy: + type: string + enum: + - invoice-amount-due + required: + - scheduleInstruction + - amountPolicy + ReminderScheduleInstruction: + type: object + description: Calculation instruction of the scheduled time. + discriminator: + propertyName: method + mapping: + date-interval: '#/components/schemas/SchedulingMethodDateInterval' + day-of-month: '#/components/schemas/SchedulingMethodDayOfMonth' + day-of-week: '#/components/schemas/SchedulingMethodDayOfWeek' + anyOf: + - $ref: '#/components/schemas/SchedulingMethodDateInterval' + - $ref: '#/components/schemas/SchedulingMethodDayOfMonth' + - $ref: '#/components/schemas/SchedulingMethodDayOfWeek' + RuleActionScheduleReminder: + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + role: + type: string + enum: + - all + - renewal + - trial-end + description: >- + Specifies the types of events that the reminder is applicable + to. + + The `renewal` and `trial-end` options are only available for + order events. + + Other events must use the `all` option. + schedule: + type: object + description: Collection of schedule instructions. + properties: + instructions: + type: array + items: + $ref: '#/components/schemas/ReminderScheduleInstruction' + chronology: + type: string + enum: + - before + - after + required: + - instructions + - chronology + required: + - role + RulesEmailNotification: + type: object + properties: + id: + type: string + format: uuid + description: ID of the message. + version: + type: string + description: >- + Version number of the message. + + Use this field to distinguish between multiple messages by name and + version number. + + If there are no versions, this field is empty. + weight: + type: integer + description: >- + Weight distribution value that is assigned to a template for a split + test. + + Each template in a split test can be assigned a weight. + + The higher the weight value, the more likely the message template is + used. + + + The split test algorithm does not assess locale when making a + weighted template selection. + minimum: 0 + maximum: 100 + example: 75 + default: 100 + templates: + description: >- + Array of message templates with language locale identifiers in [RFC + 5646](https://tools.ietf.org/html/rfc5646) format. + + A language is selected based on the customer's locale. + + + If no locale is configured for the customer, `en-US` (US English) is + used. + + + If no template is available in the customer's locale, + + a template locale is selected using a closest match algorithm. + + + If your email message templates are localized into more than one + language, + + set a customer locale. + + + Invalid placeholders render as empty strings. + + For example, `Hello {{invalid.placeholder}}!` is rendered as `Hello + !`. + type: array + minItems: 1 + items: + type: object + required: + - locale + - subject + - text + - html + - from + - to + properties: + locale: + type: string + description: >- + Language locale identifier in [RFC + 5646](https://tools.ietf.org/html/rfc5646) format. + example: fr-FR + from: + type: string + description: >- + Email address of the sender. + + + > **Important:** This value must be a verified email address. + + + To verify an email address: + 1. [Create an email delivery setting](../../Email-delivery-settings/operation/PostEmailDeliverySetting). + In the response, you receive the email and a token. + 1. [Verify the email delivery](../../Email-delivery-settings/operation/PutEmailDeliverySettingsVerification) by passing the token as the path parameter. + + Template placeholders are permitted. + + If a placeholder does not resolve to a verified `from` + address, + + the default verified `from` address is used. + maxLength: 254 + example: example@example.com + to: + type: array + description: |- + List of email addresses to which the email message is sent. + Template placeholders are permitted. + If a placeholder does not resolve to an email address, + the address is not added. + minItems: 1 + items: + type: string + maxLength: 254 + example: '{{ invoice.customer.email }}' + cc: + type: array + description: |- + List of CC email addresses to which the email message is sent. + Template placeholders are permitted. + If a placeholder does not resolve to an email address, + the address is not added. + items: + type: string + maxLength: 254 + example: '{{ invoice.customer.email }}' + bcc: + type: array + description: >- + List of BCC email addresses to which the email message is + sent. + + Template placeholders are permitted. + + If a placeholder does not resolve to an email address, + + the address is not added. + items: + type: string + maxLength: 254 + example: '{{ invoice.customer.email }}' + subject: + type: string + description: |- + Subject of the message. + The use of template placeholders is permitted for this field. + maxLength: 998 + example: Demonstration subject + text: + type: string + description: |- + Text body of the message. + To use content from the `html` field, leave this field empty. + The use of template placeholders is permitted for this field. + example: Demonstration text + html: + type: string + description: |- + HTML body of the message. + To use content from the `text` field, leave this field empty. + The use of template placeholders is permitted for this field. + example:

Demonstration text

+ editor: + type: string + description: >- + Source of the message. + + This value is required for the email editor. + + This value is not used for sending emails. + + It is used by the editor to reproduce the message for future + updates. + example:
Demonstration text
+ attachments: + description: Attachments of the email message. + type: array + items: + type: object + required: + - resourceType + - resourceId + properties: + resourceType: + type: string + description: Type of the attachment resource. + example: customer + resourceId: + type: string + description: |- + ID of the attachment resource. + Template placeholders are permitted. + maxLength: 50 + example: cus_0YV7DDSDD1C8DA64KHH2W33CPF + example: + locale: fr-FR + from: example@example.com + to: + - '{{ invoice.customer.email }}' + subject: Sujet de démonstration + text: Texte de démonstration + html:

Texte de démonstration

+ editor:
Texte de démonstration
+ required: + - templates + RuleActionSendEmail: + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + id: + type: string + format: uuid + description: ID of the action. + title: + type: string + description: Title of the messages (this title appears in reports). + emails: + type: array + description: List of messages. + minItems: 1 + items: + $ref: '#/components/schemas/RulesEmailNotification' + splitTestStartTime: + type: string + format: date-time + description: Split test start time. + required: + - emails + RuleActionStopSubscriptions: + description: Stop active subscriptions. + allOf: + - $ref: '#/components/schemas/RuleAction' + RuleActionTagOrUntagCustomer: + description: Tag or untag a customer with a specified list of tags. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + addingTags: + description: List of tag ID customer should be tagged with. + type: array + maxItems: 1000 + items: + type: string + removingTags: + description: List of tag ID customer should be untagged from. + type: array + maxItems: 1000 + items: + type: string + required: + - addingTags + - removingTags + RuleActionVoidIntuitQuickbooksInvoice: + description: Void an Intuit QuickBooks invoice. + allOf: + - $ref: '#/components/schemas/RuleAction' + - type: object + properties: + credentialHash: + type: string + description: ID of the OAuth2 credential. + required: + - credentialHash + - customerDisplayName + - unearnedRevenueAccount + Bind: + type: object + description: Rule information. + properties: + id: + description: ID of the rule. + $ref: '#/components/schemas/ResourceId' + name: + description: Name of the rule. + type: string + labels: + description: Labels of the rule. + type: array + uniqueItems: true + example: + - test-rule + - 'category:foo' + items: + type: string + pattern: '[a-zA-Z][a-zA-Z0-9:-]*' + status: + description: Status of the rule. + type: string + default: active + enum: + - active + - inactive + filter: + description: >- + Filters the collection items. + + This field requires a special format. Use `,` for multiple allowed + values. + + Use `;` for multiple fields. + + + For more information, see [Using filter with + collections](https://all-rebilly.redoc.ly/#section/Using-filter-with-collections). + type: string + actions: + description: Actions that execute when an event occurs. + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/RuleAction' + required: + - name + - actions + Rule: + type: object + allOf: + - $ref: '#/components/schemas/Bind' + - type: object + properties: + final: + description: >- + Specifies if the rule stops subsequent rules in the event list + from being executed. + type: boolean + default: true + RuleSet: + type: object + description: Ruleset for a specific event. + properties: + version: + description: Version of the ruleset. + type: integer + readOnly: true + binds: + type: array + description: |- + Binds always execute, regardless of rule based events. + A bind is a configuration of an event and one or more actions. + items: + $ref: '#/components/schemas/Bind' + rules: + type: array + description: >- + Rules can be configured to stop subsequent rules in the event list + from being executed. + + A rule is a configuration of an event and one or more actions. + items: + $ref: '#/components/schemas/Rule' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + required: + - rules + RulesEngineTimeline: + type: object + properties: + id: + type: string + description: ID of the timeline message. + readOnly: true + maxLength: 50 + example: tmln_0YV8Q9WEF5DTA8HFXS27D1G6GC + type: + description: Type of timeline message. + type: string + readOnly: true + enum: + - timeline-comment-created + - ruleset-created + - ruleset-changed + triggeredBy: + description: 'Specifies who, or what, triggered the timeline event.' + type: string + readOnly: true + enum: + - rebilly + - app + - direct-api + message: + description: Contents of the timeline message. + type: string + extraData: + $ref: '#/components/schemas/TimelineExtraData' + occurredTime: + description: Date and time when the timeline message occurred. + readOnly: true + $ref: '#/components/schemas/ServerTimestamp' + _links: + $ref: '#/components/schemas/SelfLink' + RuleSetHistoryItem: + type: object + description: Version of ruleset. + readOnly: true + properties: + version: + description: Version of the ruleset. + type: integer + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - history + - rules + RuleSetVersion: + type: object + description: Version of ruleset. + readOnly: true + properties: + version: + description: Version of the ruleset. + type: integer + binds: + type: array + description: |- + Binds always execute, regardless of rule based events. + A bind is a configuration of an event and one or more actions. + items: + $ref: '#/components/schemas/Bind' + rules: + type: array + description: >- + Rules can be configured to stop subsequent rules in the event list + from being executed. + + A rule is a configuration of an event and one or more actions. + items: + $ref: '#/components/schemas/Rule' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + RuleSetDraft: + type: object + description: Draft ruleset for specific event. + properties: + id: + type: string + readOnly: true + description: ID of the draft rule set. + maxLength: 50 + example: rule_dft_0YVDN3FN43CEQ8CQ93Y0H00ZQ7 + baseVersion: + type: integer + minimum: 0 + description: >- + Ruleset version on which the draft ruleset is based. + + Generally, a draft ruleset is cloned from an existing ruleset. + + Use this field to determine if the active ruleset changed since the + draft creation. + + `0` denotes a draft ruleset that is not created from an existing + ruleset. + + The value of this field is informational only, it is not assigned to + the active ruleset. + binds: + type: array + description: |- + Binds always execute, regardless of rule based events. + A rule is a configuration of an event and one or more actions. + items: + $ref: '#/components/schemas/Bind' + rules: + type: array + description: >- + Rule can be configured to stop subsequent rules in the event list + from being executed. + + A rule is a configuration of an event and one or more actions. + items: + $ref: '#/components/schemas/Rule' + author: + description: Author of the draft. + readOnly: true + type: object + properties: + id: + description: Author's user ID. + type: string + $ref: '#/components/schemas/ResourceId' + name: + description: Author's first and last name. + type: string + name: + type: string + description: Name of the draft. + description: + type: string + description: Detailed description of the drafted ruleset. + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - user + required: + - rules + - name + - baseVersion + ForgotPassword: + type: object + required: + - email + properties: + email: + description: Email address to which a reset password link is sent. + type: string + format: email + GatewayAccount: + description: Gateway account details. + type: object + required: + - gatewayName + - method + - acceptedCurrencies + discriminator: + propertyName: gatewayName + mapping: + A1Gateway: '#/components/schemas/A1Gateway' + ACI: '#/components/schemas/ACI' + Adyen: '#/components/schemas/Adyen' + Aircash: '#/components/schemas/Aircash' + Airpay: '#/components/schemas/Airpay' + Airwallex: '#/components/schemas/Airwallex' + AmazonPay: '#/components/schemas/AmazonPay' + AmexVPC: '#/components/schemas/AmexVPC' + ApcoPay: '#/components/schemas/ApcoPay' + AsiaPaymentGateway: '#/components/schemas/AsiaPaymentGateway' + AstroPayCard: '#/components/schemas/AstroPayCard' + AuthorizeNet: '#/components/schemas/AuthorizeNet' + Awepay: '#/components/schemas/Awepay' + Bambora: '#/components/schemas/Bambora' + BankSEND: '#/components/schemas/BankSEND' + BitPay: '#/components/schemas/BitPay' + BlueSnap: '#/components/schemas/BlueSnap' + BraintreePayments: '#/components/schemas/BraintreePayments' + Buckaroo: '#/components/schemas/Buckaroo' + CASHlib: '#/components/schemas/CASHlib' + CODVoucher: '#/components/schemas/CODVoucher' + Coinbase: '#/components/schemas/Coinbase' + CoinGate: '#/components/schemas/CoinGate' + CoinPayments: '#/components/schemas/CoinPayments' + Cardknox: '#/components/schemas/Cardknox' + Cashflows: '#/components/schemas/Cashflows' + Cashterminal: '#/components/schemas/Cashterminal' + CashToCode: '#/components/schemas/CashToCode' + CauriPayment: '#/components/schemas/CauriPayment' + Cayan: '#/components/schemas/Cayan' + CCAvenue: '#/components/schemas/CCAvenue' + Chase: '#/components/schemas/Chase' + CheckoutCom: '#/components/schemas/CheckoutCom' + Chillstock: '#/components/schemas/Chillstock' + Circle: '#/components/schemas/Circle' + Citadel: '#/components/schemas/Citadel' + Clearhaus: '#/components/schemas/Clearhaus' + Cleo: '#/components/schemas/Cleo' + Conekta: '#/components/schemas/Conekta' + Coppr: '#/components/schemas/Coppr' + Credorax: '#/components/schemas/Credorax' + Cryptonator: '#/components/schemas/Cryptonator' + CyberSource: '#/components/schemas/CyberSource' + DataCash: '#/components/schemas/DataCash' + Dengi: '#/components/schemas/Dengi' + Dimoco: '#/components/schemas/Dimoco' + dLocal: '#/components/schemas/dLocal' + Dragonphoenix: '#/components/schemas/Dragonphoenix' + Dropayment: '#/components/schemas/Dropayment' + Directa24: '#/components/schemas/Directa24' + EasyPayDirect: '#/components/schemas/EasyPayDirect' + EBANX: '#/components/schemas/EBANX' + EPG: '#/components/schemas/EPG' + EPro: '#/components/schemas/EPro' + EcorePay: '#/components/schemas/EcorePay' + Elavon: '#/components/schemas/Elavon' + EMS: '#/components/schemas/EMS' + ePay: '#/components/schemas/ePay' + Euteller: '#/components/schemas/Euteller' + Ezeebill: '#/components/schemas/Ezeebill' + eZeeWallet: '#/components/schemas/eZeeWallet' + ezyEFT: '#/components/schemas/ezyEFT' + FinTecSystems: '#/components/schemas/FinTecSystems' + Finrax: '#/components/schemas/Finrax' + Flexepin: '#/components/schemas/Flexepin' + Forte: '#/components/schemas/Forte' + FundSend: '#/components/schemas/FundSend' + GET: '#/components/schemas/GET' + Gigadat: '#/components/schemas/Gigadat' + GlobalOne: '#/components/schemas/GlobalOne' + Gooney: '#/components/schemas/Gooney' + Gpaysafe: '#/components/schemas/Gpaysafe' + Greenbox: '#/components/schemas/Greenbox' + HiPay: '#/components/schemas/HiPay' + ICEPAY: '#/components/schemas/ICEPAY' + INOVAPAY: '#/components/schemas/INOVAPAY' + Ilixium: '#/components/schemas/Ilixium' + Ingenico: '#/components/schemas/Ingenico' + Inovio: '#/components/schemas/Inovio' + InstaDebit: '#/components/schemas/InstaDebit' + Intuit: '#/components/schemas/Intuit' + IpayOptions: '#/components/schemas/IpayOptions' + JetPay: '#/components/schemas/JetPay' + Jeton: '#/components/schemas/Jeton' + JPMOrbital: '#/components/schemas/JPMOrbital' + Khelocard: '#/components/schemas/Khelocard' + Klarna: '#/components/schemas/Klarna' + Konnektive: '#/components/schemas/Konnektive' + loonie: '#/components/schemas/loonie' + LPG: '#/components/schemas/LPG' + MaxiCash: '#/components/schemas/MaxiCash' + MercadoPago: '#/components/schemas/MercadoPago' + MiFinity: '#/components/schemas/MiFinity' + MobilePay: '#/components/schemas/MobilePay' + Moneris: '#/components/schemas/Moneris' + MtaPay: '#/components/schemas/MtaPay' + MuchBetter: '#/components/schemas/MuchBetter' + MuchBetterGateway: '#/components/schemas/MuchBetterGateway' + MyFatoorah: '#/components/schemas/MyFatoorah' + Neosurf: '#/components/schemas/Neosurf' + NMI: '#/components/schemas/NMI' + Netbanking: '#/components/schemas/Netbanking' + Neteller: '#/components/schemas/Neteller' + NGenius: '#/components/schemas/NGenius' + NinjaWallet: '#/components/schemas/NinjaWallet' + NordikCoin: '#/components/schemas/NordikCoin' + NOWPayments: '#/components/schemas/NOWPayments' + NuaPay: '#/components/schemas/NuaPay' + OchaPay: '#/components/schemas/OchaPay' + Onlineueberweisen: '#/components/schemas/Onlineueberweisen' + OnRamp: '#/components/schemas/OnRamp' + Orbital: '#/components/schemas/Orbital' + Pagadito: '#/components/schemas/Pagadito' + Pagsmile: '#/components/schemas/Pagsmile' + Panamerican: '#/components/schemas/Panamerican' + PandaGateway: '#/components/schemas/PandaGateway' + ParamountCommerce: '#/components/schemas/ParamountCommerce' + ParamountEft: '#/components/schemas/ParamountEft' + ParamountInterac: '#/components/schemas/ParamountInterac' + Pay4Fun: '#/components/schemas/Pay4Fun' + PayCash: '#/components/schemas/PayCash' + PayClub: '#/components/schemas/PayClub' + PayEcards: '#/components/schemas/PayEcards' + Paynote: '#/components/schemas/Paynote' + PayPal: '#/components/schemas/PayPal' + Payeezy: '#/components/schemas/Payeezy' + Payflow: '#/components/schemas/Payflow' + PaymentAsia: '#/components/schemas/PaymentAsia' + PaymenTechnologies: '#/components/schemas/PaymenTechnologies' + PaymentsOS: '#/components/schemas/PaymentsOS' + Paymero: '#/components/schemas/Paymero' + Payper: '#/components/schemas/Payper' + Payr: '#/components/schemas/Payr' + PayRedeem: '#/components/schemas/PayRedeem' + PayRetailers: '#/components/schemas/PayRetailers' + Paysafe: '#/components/schemas/Paysafe' + Paysafecard: '#/components/schemas/Paysafecard' + Paysafecash: '#/components/schemas/Paysafecash' + PayTabs: '#/components/schemas/PayTabs' + PayULatam: '#/components/schemas/PayULatam' + Payvision: '#/components/schemas/Payvision' + PharosPayments: '#/components/schemas/PharosPayments' + Piastrix: '#/components/schemas/Piastrix' + Pin4Pay: '#/components/schemas/Pin4Pay' + Plugnpay: '#/components/schemas/Plugnpay' + PostFinance: '#/components/schemas/PostFinance' + PSiGate: '#/components/schemas/PSiGate' + PPRO: '#/components/schemas/PPRO' + Prosa: '#/components/schemas/Prosa' + Rapyd: '#/components/schemas/Rapyd' + RPN: '#/components/schemas/RPN' + Realex: '#/components/schemas/Realex' + Realtime: '#/components/schemas/Realtime' + Redsys: '#/components/schemas/Redsys' + Rotessa: '#/components/schemas/Rotessa' + Safecharge: '#/components/schemas/Safecharge' + SaltarPay: '#/components/schemas/SaltarPay' + SMSVoucher: '#/components/schemas/SMSVoucher' + Sofort: '#/components/schemas/Sofort' + Sagepay: '#/components/schemas/Sagepay' + SeamlessChex: '#/components/schemas/SeamlessChex' + SecureTrading: '#/components/schemas/SecureTrading' + SecurionPay: '#/components/schemas/SecurionPay' + Skrill: '#/components/schemas/Skrill' + SmartInvoice: '#/components/schemas/SmartInvoice' + SparkPay: '#/components/schemas/SparkPay' + StaticGateway: '#/components/schemas/StaticGateway' + STPMexico: '#/components/schemas/STPMexico' + Stripe: '#/components/schemas/Stripe' + Telr: '#/components/schemas/Telr' + TestProcessor: '#/components/schemas/TestProcessor' + ToditoCash: '#/components/schemas/ToditoCash' + Truevo: '#/components/schemas/Truevo' + TrustsPay: '#/components/schemas/TrustsPay' + Trustly: '#/components/schemas/Trustly' + TWINT: '#/components/schemas/TWINT' + UPayCard: '#/components/schemas/UPayCard' + USAePay: '#/components/schemas/USAePay' + VantivLitle: '#/components/schemas/VantivLitle' + VCreditos: '#/components/schemas/VCreditos' + VegaWallet: '#/components/schemas/VegaWallet' + Wallet88: '#/components/schemas/Wallet88' + Walpay: '#/components/schemas/Walpay' + WesternUnion: '#/components/schemas/WesternUnion' + Wirecard: '#/components/schemas/Wirecard' + WorldlineAtosFrankfurt: '#/components/schemas/WorldlineAtosFrankfurt' + Worldpay: '#/components/schemas/Worldpay' + Zotapay: '#/components/schemas/Zotapay' + eMerchantPay: '#/components/schemas/eMerchantPay' + ecoPayz: '#/components/schemas/ecoPayz' + iCanPay: '#/components/schemas/iCanPay' + iCheque: '#/components/schemas/iCheque' + iDebit: '#/components/schemas/iDebit' + vegaaH: '#/components/schemas/vegaaH' + XPay: '#/components/schemas/XPay' + Zimpler: '#/components/schemas/Zimpler' + properties: + id: + type: string + description: ID of the payment gateway account. + readOnly: true + maxLength: 50 + example: gw_acc_0YVCXMF26DDNKAERE5NW727S34 + gatewayName: + $ref: '#/components/schemas/GatewayName' + acquirerName: + default: Other + allOf: + - $ref: '#/components/schemas/AcquirerName' + method: + $ref: '#/components/schemas/PaymentMethod' + acceptedCurrencies: + description: Accepted currencies. An array of ISO 4217 currency codes. + type: array + items: + type: string + paymentCardSchemes: + description: Accepted payment card brands. + type: array + items: + $ref: '#/components/schemas/PaymentCardBrand' + status: + description: Status of the gateway account. + readOnly: true + type: string + enum: + - active + - inactive + - pending + - closed + merchantCategoryCode: + description: Merchant category code of the payment gateway account. + type: string + pattern: '^[0-9]{4}$' + default: '0000' + dccMarkup: + description: Dynamic currency conversion markup in basis points. + type: + - integer + - 'null' + minimum: -10000 + maximum: 10000 + dccForceCurrency: + type: + - string + - 'null' + description: >- + Forces Dynamic Currency Conversion (DCC) to the specified currency + on each sale. + + To disable forced DCC, leave this field empty. + dccForceRounding: + type: boolean + default: false + description: >- + Specifies that DCC quote amounts must be rounded half up to the + nearest whole number. + descriptor: + description: Gateway account descriptor value. + type: + - string + - 'null' + cityField: + description: >- + Gateway account city field. This value is also known as a line 2 + descriptor. + type: + - string + - 'null' + excludedDccQuoteCurrencies: + description: Excluded Dynamic Currency Conversion (DCC) quote currencies. + type: array + items: + type: string + monthlyLimit: + description: >- + Monthly limit on the amount money that the gateway account can + process. + type: + - number + - 'null' + format: double + minimum: 0 + approvalWindowTtl: + description: >- + Allotted time, in seconds, in which an offsite transaction must be + approved before it is automatically `abandoned`. + type: integer + default: 3600 + minimum: 300 + maximum: 16777215 + reconciliationWindowEnabled: + description: >- + Specifies that if a transaction is not reconciled within the + `reconciliationWindowTtl` time, the transaction is marked as + `abandoned`. + type: boolean + default: false + reconciliationWindowTtl: + description: >- + Allotted time, in seconds, in which a reconciliation must occur + before it is automatically `abandoned`. + type: + - integer + - 'null' + minimum: 300 + maximum: 16777215 + threeDSecure: + description: Specifies if a gateway account allows 3D Secure. + type: boolean + default: false + dynamicDescriptor: + description: Specifies if a gateway allows dynamic descriptors. + type: boolean + default: false + digitalWallets: + $ref: '#/components/schemas/DigitalWallets' + isDown: + description: Specifies if a gateway is in a downtime period. + type: boolean + readOnly: true + additionalFilters: + description: >- + Additional filters used to determine if the gateway account can be + selected to process a transaction. + + + For example, the filter may place a maximum amount value on + transaction. + + If a transaction exceeds this value, the gateway account is not + used. + + + For more information see, + + [Using + filters](https://all-rebilly.redoc.ly/#section/Using-filter-with-collections). + type: + - string + - 'null' + example: 'amount:1..100;bin:411111,444433' + timeout: + description: 'Gateway account request timeout, in seconds.' + type: + - integer + - 'null' + minimum: 10 + maximum: 120 + token: + description: Gateway account token. + type: + - string + - 'null' + readOnly: true + example: TwiX3f92k4AiBE27BzTbQ38hHjicBz_w + sticky: + description: >- + Specifies if all future payments by the customer's payment + instrument are processed by this gateway account. + + + For more information, see [Gateway + routing](https://www.rebilly.com/docs/settings/intelligent-payment-routing/#sticky-gateway-accounts). + type: boolean + default: true + setupInstruction: + type: string + default: do-nothing + description: >- + Creates zero, one, or more child transactions such as `authorize` + and `void`. + + The transactions are linked to the `setup` transaction by the + `parentTransactionId` relationship. + enum: + - authorize + - authorize-and-void + - sca + - do-nothing + x-enumDescriptions: + authorize: >- + Creates an `authorize` transaction in the amount and currency of + the request. + + This is used when a gateway account is configured for Strong + Customer Authentication (SCA). + authorize-and-void: >- + Creates an `authorize` transaction in the amount and currency of + the request, + + followed by a `void`, if the `authorize` is approved. + + This is used when a gateway account is configured for Strong + Customer Authentication (SCA). + sca: >- + Uses Strong Customer Authentication (SCA) without an `authorize` + transaction. + + SCA includes 3DS, and specific wallet behavior, + + such as setting up a billing agreement with PayPal. + do-nothing: |- + Does nothing except return an approved `setup` transaction. + This is the default behavior. + readyToPayoutInstruction: + type: string + default: none + description: >- + Defines the mode of gateway payout behaviour for the [Ready to + payout](https://all-rebilly.redoc.ly/tag/Purchases/operation/StorefrontPostReadyToPayout) + operation. + enum: + - all + - covered-payout + - approved-payment + - none + x-enumDescriptions: + all: Payout any amount from this gateway. + covered-payout: >- + Payout from this gateway if it previously processed a payment for + the same, + + or a greater, amount. + approved-payment: >- + Payout any amount from this gateway if it processed an earlier + payment. + + The customer must have a previously approved transaction, + + in the same currency, on this gateway. + none: >- + Do not allow any payouts with this gateway. This is the default + value, merchants must opt into payouts. + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + organizationId: + deprecated: true + readOnly: true + allOf: + - $ref: '#/components/schemas/OrganizationId' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - onBoardingUrl + - dynamicIpnUrl + - staticIpnUrl + A1Gateway: + description: A1Gateway gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: A1Gateway credentials object. + properties: + accountId: + type: string + description: ID of the A1Gateway account. + password: + type: string + description: A1Gateway password. + format: password + writeOnly: true + required: + - accountId + - password + ThreeDSecureIO3dsServer: + description: ThreeDSecureIO 3DS Server. + type: object + required: + - name + - acquirerMerchantIdVisa + - acquirerMerchantIdMastercard + - merchantName + - merchantAcquirerBinVisa + - merchantAcquirerBinMastercard + - merchantCountry + - merchantUrl + - transactionType + properties: + name: + type: string + description: Merchant plug-in name. + enum: + - ThreeDSecureIO3dsServer + acquirerMerchantIdVisa: + type: string + description: Merchant ID (MID) of the Visa acquirer. + maxLength: 35 + acquirerMerchantIdMastercard: + type: string + description: Merchant ID (MID) of the Mastercard acquirer. + maxLength: 35 + acquirerMerchantIdAmex: + type: string + description: Merchant ID (MID) of the American Express acquirer. + maxLength: 35 + acquirerMerchantIdDiscover: + type: string + description: Merchant ID (MID) of the Discover/Diners acquirer. + maxLength: 35 + acquirerMerchantIdJcb: + type: string + description: Merchant ID (MID) of the JCB acquirer. + maxLength: 35 + merchantName: + type: string + description: Name of the merchant. + maxLength: 40 + merchantAcquirerBinVisa: + type: string + description: BIN of the Visa acquirer. + minLength: 6 + maxLength: 11 + merchantAcquirerBinMastercard: + type: string + description: BIN of the Mastercard acquirer. + minLength: 6 + maxLength: 11 + merchantAcquirerBinAmex: + type: string + description: BIN of the American Express acquirer. + minLength: 6 + maxLength: 11 + merchantAcquirerBinDiscover: + type: string + description: BIN of the Discover/Diners acquirer. + minLength: 6 + maxLength: 11 + merchantAcquirerBinJcb: + type: string + description: BIN of the JCB acquirer. + minLength: 6 + maxLength: 11 + merchantCountry: + type: string + description: Merchant country in ISO Alpha-2 code format. + maxLength: 2 + example: US + merchantUrl: + type: string + description: URL of the merchant's website. + maxLength: 2048 + transactionType: + type: string + enum: + - '01' + - '03' + - '10' + - '11' + - '28' + description: Identifies the type of transaction that is being authenticated. + x-enumDescriptions: + '1': Goods/Service purchase. + '3': Check acceptance. + '10': Account funding. + '11': Quasi-Cash transaction. Indicates the type of 3RI request. + '28': Prepaid activation and load. + declineNotEnrolled: + type: boolean + description: >- + Specifies whether to decline transactions if a payment card is not + enrolled. + default: false + use3dsForMerchantInitiated: + type: boolean + description: Specifies whether to use 3DS for merchant initiated transactions. + default: false + threeRIInd: + type: string + enum: + - '01' + - '02' + - '03' + - '04' + - '05' + - '06' + - '07' + - 08 + - 09 + - '10' + - '11' + description: |- + Indicates the type of 3RI request. + Values 06 - 11 are only supported in 3DS 2.2.0. + x-enumDescriptions: + '10': Whitelist status check. + '11': Other payment. + '01': Recurring transaction. + '02': Instalment transaction. + '03': Add card. + '04': Maintain card information. + '05': Account verification. + '06': Split/delayed shipment. + '07': Top-up. + 08: Mail order. + 09: Telephone order. + ACI: + description: ACI gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: ACI credentials object. + properties: + entityId: + type: string + accessToken: + type: string + format: password + writeOnly: true + required: + - entityId + - accessToken + settings: + type: object + description: ACI settings object. + properties: + url: + type: string + description: ACI custom server URL. + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + Adyen: + description: Adyen gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + description: Adyen credentials object. + properties: + merchantAccount: + type: string + description: Adyen merchant account. + apiKey: + type: string + description: Adyen API key. + format: password + writeOnly: true + required: + - merchantAccount + - apiKey + settings: + type: object + description: Adyen settings object. + properties: + store: + type: string + description: >- + E-commerce or point-of-sale store that is processing the + payment. + minLength: 1 + maxLength: 16 + url: + type: string + description: Adyen post URL. + splitPayments: + type: array + description: Adyen split payments. + items: + type: object + properties: + percentage: + description: Percentage of the transaction amount. + type: number + format: double + minimum: 0 + maximum: 100 + example: 0.5 + commissionAmount: + description: Fixed commission amount in minor units. + type: number + format: integer + example: 100 + currency: + $ref: '#/components/schemas/CurrencyCode' + account: + description: >- + ID of the balance account where the split amount is + sent. + type: string + merchantCountry: + description: >- + Two-letter ISO 3166 alpha-2 code of the merchant's + country. + type: string + example: US + type: + description: Account type of the split payment. + type: string + enum: + - BalanceAccount + - Commission + - Remainder + - PaymentFee + totalTaxRate: + type: number + format: double + description: >- + Total tax percentage as a decimal. Use this field to + calculate the total tax amount to send with level 2 enhanced + scheme data. + example: 5.5 + minimum: 0.01 + maximum: 100 + enableMoto: + type: boolean + description: Specifies whether to use Mail Order Telephone Order (MOTO). + default: false + example: false + required: + - url + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + Aircash: + description: Aircash gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Aircash credentials object. + properties: + partnerId: + type: string + description: ID of the Aircash partner. + privateKey: + writeOnly: true + type: string + description: Private key of the Aircash partner. + format: password + x-multiline: true + privateKeyPassword: + writeOnly: true + type: string + description: Private key password of the Aircash partner. + format: password + required: + - partnerId + - privateKey + - privateKeyPassword + Airpay: + description: Airpay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Airpay credentials object. + properties: + username: + type: string + description: Airpay username. + merchantId: + type: string + description: ID of the Airpay merchant. + password: + writeOnly: true + type: string + description: Airpay password. + format: password + apiKey: + type: string + description: Airpay API key. + format: password + writeOnly: true + required: + - username + - merchantId + - password + - apiKey + Airwallex: + description: Airwallex gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Airwallex credentials object. + properties: + clientId: + type: string + description: Airwallex client ID. + apiKey: + type: string + description: Airwallex API key. + format: password + writeOnly: true + required: + - clientId + - apiKey + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + AmazonPay: + description: AmazonPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantId: + type: string + description: ID of the AmazonPay merchant. + storeId: + type: string + description: ID of the AmazonPay store. + publicKeyId: + type: string + description: ID of the AmazonPay public key. + privateKey: + description: ID of the AmazonPay private key. + type: string + format: password + x-multiline: true + writeOnly: true + required: + - privateKey + - storeId + - publicKeyId + - merchantId + AmexVPC: + description: AmexVPC gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + description: AmexVPC credentials object. + properties: + merchantId: + type: string + description: ID of the AmexVPC merchant. + accessCode: + type: string + description: AmexVPC access Code. + format: password + writeOnly: true + user: + type: string + description: >- + AmexVPC account user. This field is used for refund, void, + and capture transactions. + password: + type: string + description: >- + AmexVPC account password. This field is used for refund, + void, and capture transactions. + format: password + writeOnly: true + required: + - merchantId + - accessCode + - user + - password + settings: + type: object + description: AmexVPC settings object. + properties: + url: + type: string + description: Virtual payment client URL. + required: + - url + ApcoPay: + description: ApcoPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: ApcoPay credentials object. + properties: + profileID: + type: string + description: ID of the ApcoPay profile. + secretWord: + type: string + description: ApcoPay secret word. + format: password + writeOnly: true + MerchantID: + type: string + description: ID of the ApcoPay merchant. + MerchantPassword: + type: string + description: ApcoPay merchant password. + format: password + writeOnly: true + required: + - profileID + - secretWord + - MerchantID + - MerchantPassword + settings: + type: object + properties: + method: + type: string + description: ApcoPay method. + enum: + - AFTERPAY + - BANCONTACT + - CREDITCLICK + - FLEXEPIN + - IDEAL + - JPAY + - OCTAPAY + - ONLINEUBERWEISEN + - ORIENTALWALLET + - VENUSPOINT + - ZIMPLER + required: + - method + AsiaPaymentGateway: + description: AsiaPaymentGateway gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: AsiaPaymentGateway credentials object. + properties: + merchantNumber: + type: string + description: AsiaPaymentGateway merchant number. + secretKey: + type: string + description: AsiaPaymentGateway secret key for hash. + format: password + writeOnly: true + required: + - merchantNumber + - secretKey + settings: + type: object + properties: + use3DSEndpoint: + type: boolean + description: Specifies whether to use a 3DS endpoint. + AstroPayCard: + description: AstroPay Card gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: AstroPay Card credentials object. + properties: + x_login: + type: string + description: AstroPay Card login. + x_tran_key: + type: string + description: AstroPay Card transaction key. + format: password + writeOnly: true + secret_key: + type: string + description: AstroPay Card secret key. + format: password + writeOnly: true + api_key: + type: string + description: OneTouch Astropay API key. + format: password + writeOnly: true + required: + - x_login + - x_tran_key + - secret_key + settings: + type: object + description: AstroPay Card settings object. + properties: + oneTouchApi: + type: boolean + description: Specifies whether to use a OneTouch AstroPay API. + default: false + useOneTouchSdk: + type: boolean + description: Specifies whether to use the OneTouch AstroPay SDK. + default: false + merchantName: + type: string + description: Displayed merchant name for AstroPay OneTouch. + merchantLogoUrl: + type: string + description: Displayed merchant logo for AstroPay OneTouch. + AuthorizeNet: + description: AuthorizeNet gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + apiLoginId: + type: string + description: ID of the AuthorizeNet API login. + transactionKey: + description: AuthorizeNet transaction key. + type: string + format: password + writeOnly: true + required: + - apiLoginId + - transactionKey + Awepay: + description: Awepay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Awepay credentials object. + properties: + sid: + type: string + description: ID of the Awepay site. + rcode: + type: string + description: Awepay rcode. + format: password + writeOnly: true + secretKey: + type: string + description: Awepay secret key for the P2P REST API. + format: password + writeOnly: true + required: + - sid + - rcode + settings: + type: object + description: Awepay setting. + properties: + useP2pRest: + type: boolean + description: Specifies whether to use the P2P REST API. + Bambora: + description: Bambora gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantId: + type: string + description: ID of the Bambora merchant. + apiPasscode: + type: string + description: Bambora API passcode. + format: password + writeOnly: true + required: + - merchantId + - apiPasscode + BankSEND: + description: BankSEND gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: BankSEND credentials object. + properties: + merchantId: + description: ID of the BankSEND merchant. + type: string + merchantToken: + type: string + format: password + description: BankSEND merchant token. + writeOnly: true + required: + - merchantId + - merchantToken + BitPay: + description: BitPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: BitPay credentials object. + properties: + token: + type: string + description: BitPay merchant API token. + format: password + writeOnly: true + required: + - token + BlueSnap: + description: BlueSnap gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: BlueSnap credentials object. + properties: + username: + type: string + description: API username of the BlueSnap merchant. + merchantId: + type: string + description: ID of the BlueSnap merchant. + password: + type: string + description: API password of the BlueSnap merchant. + format: password + writeOnly: true + dataProtectionKey: + type: string + description: Data protection key of the BlueSnap merchant. + format: password + writeOnly: true + required: + - username + - password + settings: + type: object + properties: + enableMoto: + type: boolean + description: Specifies whether to use Mail Order Telephone Order (MOTO). + default: false + salesTaxAmount: + type: number + format: double + description: Sales tax amount as a decimal to send with level 2/3 data. + example: 0.06 + metadataCustomField: + type: string + description: >- + Rebilly custom field to use for sending transaction + metadata. + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + BraintreePayments: + description: BraintreePayments gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + publicKey: + type: string + description: BraintreePayments public key. + privateKey: + type: string + description: BraintreePayments private key. + format: password + writeOnly: true + merchantId: + type: string + description: ID of the BraintreePayments merchant. + format: password + writeOnly: true + merchantAccountId: + type: string + description: ID of the BraintreePayments merchant account. + format: password + writeOnly: true + required: + - publicKey + - privateKey + - merchantId + - merchantAccountId + Buckaroo: + description: Buckaroo gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Buckaroo credentials object. + properties: + websiteKey: + type: string + description: Website key. + secretKey: + description: Secret key. + type: string + format: password + required: + - websiteKey + - secretKey + CASHlib: + description: CASHlib gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: CASHlib credentials object. + properties: + apiKey: + type: string + description: CASHlib API key. + format: password + writeOnly: true + merchantId: + type: string + description: ID of the CASHlib merchant. + required: + - apiKey + - merchantId + CODVoucher: + description: CODVoucher gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: CODVoucher credentials object. + properties: + apiKey: + type: string + description: CODVoucher API key. + format: password + writeOnly: true + apiSecret: + type: string + description: CODVoucher API secret. + format: password + writeOnly: true + required: + - apiKey + - apiSecret + AmountAdjustmentTolerance: + type: integer + description: Tolerance percentage for the settled amount. + minimum: 0 + maximum: 5 + example: 5 + Coinbase: + description: Coinbase gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Coinbase credentials object. + properties: + apiKey: + type: string + format: password + writeOnly: true + required: + - apiKey + settings: + type: object + description: Coinbase settings object. + required: + - tolerancePercentage + properties: + tolerancePercentage: + $ref: '#/components/schemas/AmountAdjustmentTolerance' + CoinGate: + description: CoinGate gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + properties: + authToken: + type: string + format: password + description: Authentication token. + writeOnly: true + required: + - authToken + settings: + type: object + properties: + targetCurrency: + description: |- + Currency to which the received cryptocurrency is converted. + This field defaults to the transaction currency. + type: string + minLength: 3 + maxLength: 3 + example: USD + tolerancePercentage: + $ref: '#/components/schemas/AmountAdjustmentTolerance' + CoinPayments: + description: CoinPayments gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: CoinPayments credentials object. + required: + - publicKey + - privateKey + - ipnSecret + properties: + publicKey: + type: string + description: CoinPayments public key. + privateKey: + type: string + format: password + description: CoinPayments private key. + writeOnly: true + ipnSecret: + type: string + format: password + description: CoinPayments IPN secret. + writeOnly: true + merchantId: + type: string + format: password + description: ID of the CoinPayments merchant. + writeOnly: true + settings: + type: object + properties: + useCallbackAddress: + type: boolean + description: Specifies whether to use a callback address payment type. + Cardknox: + description: Cardknox gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + xKey: + type: string + description: Cardknox xKey. + format: password + writeOnly: true + required: + - xKey + Cashflows: + description: Cashflows gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + authenticationId: + type: string + description: ID of the Cashflows authentication. + authPassword: + type: string + description: ID of the Cashflows authentication password. + format: password + writeOnly: true + required: + - authPassword + - authId + Cashterminal: + description: Cashterminal gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + secretKey: + description: Cashterminal secret key. + type: string + format: password + writeOnly: true + required: + - secretKey + CashToCode: + description: CashToCode gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: CashToCode credentials object. + properties: + cashToCodeUsername: + type: string + description: CashToCode credentials username. + cashToCodePassword: + type: string + description: CashToCode credentials password. + format: password + writeOnly: true + merchantUsername: + type: string + description: Merchant credentials username. + merchantPassword: + type: string + description: Merchant credentials password. + format: password + writeOnly: true + mid: + type: string + description: CashToCode Merchant Identifier Number (MID) or brand name. + required: + - cashToCodeUsername + - cashToCodePassword + - merchantUsername + - merchantPassword + settings: + type: object + properties: + baseUrl: + type: string + description: Base API URL. + skipAmountSelection: + type: boolean + description: Specifies whether to skip the amount selection screen. + default: false + amounts: + type: array + description: Amounts for which transactions are processed. + items: + type: number + format: double + minimum: 0 + CauriPayment: + description: CauriPayment gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: CauriPayment credentials object. + properties: + publicKey: + type: string + description: CauriPayment merchant public key. + privateKey: + type: string + description: CauriPayment merchant private key. + format: password + writeOnly: true + required: + - publicKey + - privateKey + Cayan: + description: Cayan gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantSiteId: + type: string + description: ID of the Cayan merchant site. + merchantName: + type: string + description: Cayan merchant name. + merchantKey: + type: string + description: Cayan merchant key. + format: password + writeOnly: true + required: + - merchantSiteId + - merchantName + - merchantKey + CCAvenue: + description: CCAvenue gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: CCAvenue credentials object. + properties: + merchantId: + type: string + description: ID of the CCAvenue merchant. + accessCode: + type: string + description: CCAvenue access code. + format: password + writeOnly: true + workingKey: + type: string + description: CCAvenue working key. + format: password + writeOnly: true + required: + - merchantId + - accessCode + - workingKey + settings: + type: object + properties: + useStandingInstructionApi: + description: Specifies whether to use the Standing Instruction API. + type: boolean + default: false + Chase: + description: Chase gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + username: + type: string + description: Chase NetConnect username. + password: + type: string + description: Chase NetConnect password. + format: password + writeOnly: true + coNumber: + type: string + description: Chase CO number used for delimited file reports. + divisionId: + type: string + description: ID of the Chase division. + partialAuth: + type: boolean + description: Specifies whether to support partial authentications. + default: false + required: + - username + - password + - coNumber + - divisionId + - partialAuth + CheckoutCom: + description: Checkout.com gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Checkout.com credentials object. + properties: + secretKey: + type: string + format: password + description: Checkout.com secret API key. + writeOnly: true + required: + - secretKey + settings: + type: object + properties: + markAsWaitingGatewayOnPendingPayout: + type: boolean + description: >- + Specifies whether to mark pending payouts as + 'waiting-gateway/unknown' instead of 'approved'. + default: false + subEntityIdWebsiteCustomField: + type: string + description: >- + Name of the website custom field that contains the + sub-entity ID. + processingChannelId: + type: string + description: Checkout.com processing channel ID. + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + Chillstock: + description: Chillstock gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Chillstock credentials object. + properties: + merchantId: + type: string + description: Merchant ID (MID). + privateKey: + type: string + description: Chillstock private key. + format: password + x-multiline: true + writeOnly: true + required: + - merchantId + - privateKey + settings: + type: object + properties: + sandbox: + type: boolean + description: Specifies if the gateway account is forced to sandbox mode. + default: false + Circle: + description: Circle gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Circle credentials object. + properties: + apiKey: + type: string + format: password + description: Circle API key. + writeOnly: true + required: + - apiKey + Citadel: + description: Citadel gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Citadel credentials object. + properties: + storeName: + type: string + description: Citadel store name. + storeId: + type: string + description: ID of the Citadel store. + username: + type: string + description: Citadel username. + password: + type: string + description: Citadel password. + format: password + writeOnly: true + required: + - storeName + - storeId + - username + - password + Clearhaus: + description: Clearhaus gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + apiKey: + type: string + description: Clearhaus API key. + format: password + writeOnly: true + required: + - apiKey + Cleo: + description: Cleo gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Cleo credentials object. + properties: + accessToken: + type: string + description: Cleo access token. + format: password + writeOnly: true + required: + - accessToken + Conekta: + description: Conekta gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Conekta credentials object. + properties: + apiKey: + type: string + description: Conekta private API key. + format: password + writeOnly: true + required: + - apiKey + Coppr: + description: Coppr gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Coppr credentials object. + properties: + organizationId: + type: string + description: ID of the Coppr organization. + apiKey: + type: string + description: Coppr API key. + format: password + writeOnly: true + required: + - organizationId + - apiKey + settings: + type: object + description: Coppr settings object. + properties: + rebillyPublishableKey: + type: string + description: >- + Rebilly publishable API key. + + If this value is provided, a payment token is created for + each transaction. + + To obtain an API key, see [Manage API + keys](https://www.rebilly.com/docs/dev-docs/api-keys/#manage-api-keys). + Credorax: + description: Credorax gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantId: + type: string + description: ID of the Credorax merchant. + merchantMd5Signature: + type: string + description: Credorax MD5 signature. + format: password + writeOnly: true + required: + - merchantId + - merchantMd5Signature + Cryptonator: + description: Cryptonator gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Cryptonator credentials object. + properties: + merchant_id: + type: string + description: ID of the Cryptonator merchant. + secret: + type: string + description: Cryptonator secret. + format: password + writeOnly: true + required: + - merchant_id + - secret + CyberSource: + description: CyberSource gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: CyberSource credentials object. + properties: + accessKey: + type: string + description: CyberSource access key. + profileId: + type: string + description: ID of the CyberSource profile. + secretKey: + type: string + description: CyberSource secret key. + format: password + writeOnly: true + required: + - accessKey + - profileId + - secretKey + DataCash: + description: DataCash gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + client: + type: string + description: DataCash client. + password: + type: string + description: DataCash password. + format: password + writeOnly: true + reportGroup: + type: string + description: DataCash report group. + reportUser: + type: string + description: DataCash report user. + reportPassword: + type: string + description: DataCash report password. + format: password + writeOnly: true + visaPayoutsClient: + type: string + description: >- + DataCash client for Visa OCT (Original Credit Transfer) + payouts. + visaPayoutsPassword: + type: string + description: >- + DataCash password for Visa OCT (Original Credit Transfer) + payouts. + format: password + writeOnly: true + masterCardPayoutsClient: + type: string + description: >- + DataCash client for MasterCard OCT (Original Credit + Transfer) payouts. + masterCardPayoutsPassword: + type: string + description: >- + DataCash password for MasterCard OCT (Original Credit + Transfer) payouts. + format: password + writeOnly: true + required: + - client + - password + settings: + type: object + description: Datacash settings object. + properties: + policy: + type: integer + description: Policy. + minimum: 0 + maximum: 7 + default: 2 + delay: + type: integer + description: Automatic capture delay in hours. + minimum: 0 + default: 0 + Dengi: + description: Dengi gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + projectId: + type: string + description: ID of the Dengi project. + publicKey: + type: string + description: Dengi public key. + format: password + writeOnly: true + refundKey: + type: string + description: Dengi refund key. + format: password + writeOnly: true + required: + - projectId + - publicKey + - refundKey + Dimoco: + description: Dimoco gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantId: + type: string + description: ID of the Dimoc merchant. + orderId: + type: string + description: ID of the Dimoc project. + password: + type: string + format: password + description: ID of the Dimoc project. + writeOnly: true + required: + - merchantId + - orderId + - password + dLocal: + description: dLocal gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + xLogin: + type: string + description: dLocal login. + xTransKey: + type: string + description: dLocal transKey. + secretKey: + type: string + format: password + description: dLocal secret key. + writeOnly: true + xPayoutLogin: + type: string + description: dLocal payout login. + xPayoutTransKey: + type: string + description: dLocal payout transKey. + payoutSecretKey: + type: string + format: password + description: dLocal payout secret key. + writeOnly: true + required: + - xLogin + - xTransKey + - secretKey + settings: + type: object + description: dLocal settings object. + properties: + createInstallmentPlan: + type: boolean + default: false + description: >- + Specifies whether to create an installment plan and use it + for payment. + customerDocumentCustomField: + type: string + description: >- + Name of the custom field which contains customer document. + + Use this setting to skip the intermediate step of collecting + the customer document for payment card transactions. + Dragonphoenix: + description: Dragonphoenix gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Dragonphoenix credentials object. + properties: + sid: + type: string + description: ID of the Dragonphoenix site. + rcode: + type: string + description: Dragonphoenix rcode. + format: password + writeOnly: true + required: + - sid + - rcode + Dropayment: + description: Dropayment gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Dropayment credentials object. + properties: + login: + type: string + description: Dropayment login. + endpointId: + type: string + description: ID of the Dropayment end point. + secretKey: + type: string + description: Dropayment secret key. + format: password + writeOnly: true + required: + - login + - endpointId + - secretKey + Directa24Banks: + type: string + enum: + - AA + - AL + - AZ + - B + - BAB + - BB + - BC + - BE + - BL + - BM + - BN + - BP + - BQ + - BU + - BV + - BW + - BX + - BZ + - CA + - CE + - CI + - CU + - EF + - EN + - EY + - FA + - FB + - FC + - GC + - GG + - HC + - I + - IA + - IB + - JM + - LC + - LE + - LL + - MC + - ME + - MD + - MP + - MT + - NB + - OM + - OX + - PC + - PH + - PL + - SB + - SC + - SE + - SF + - SM + - SS + - ST + - SU + - TC + - TK + - TG + - TR + - TY + - RY + - UB + - UI + - UL + - US + - VD + - VI + - WA + - WP + - WU + - XA + Directa24: + description: Directa24 gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Directa24 credentials object. + properties: + x_login: + type: string + description: Directa24 login. + x_tran_key: + type: string + description: Directa24 transaction key. + format: password + writeOnly: true + secret_key: + type: string + description: Directa24 secret key. + format: password + writeOnly: true + web_pay_login: + type: string + description: Directa24 web pay status login. + web_pay_tran_key: + type: string + description: Directa24 web pay status password. + format: password + writeOnly: true + cashout_login: + type: string + description: Directa24 cashout login. + cashout_password: + type: string + description: Directa24 cashout password. + format: password + writeOnly: true + required: + - x_login + - x_tran_key + - secret_key + - web_pay_login + - web_pay_tran_key + settings: + type: object + description: Directa24 settings object. + properties: + banks: + type: array + description: List of banks that is displayed to customers. + items: + $ref: '#/components/schemas/Directa24Banks' + skipStep: + type: boolean + description: >- + Specifies whether to skip the enter user personal + information step. + useV3Api: + type: boolean + description: Specifies whether to use Directa24 API version 3. + storeIdNumber: + type: boolean + description: >- + Specifies whether to store the customer ID number for future + use. + EasyPayDirect: + description: EasyPayDirect gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: EasyPayDirect credentials object. + properties: + secretKey: + type: string + description: API secret key of the EasyPayDirect account. + format: password + writeOnly: true + required: + - secretKey + EBANX: + description: EBANX gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: EBANX credentials object. + properties: + integrationKey: + type: string + description: EBANX integration key. + format: password + writeOnly: true + required: + - integrationKey + EPG: + description: EPG gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: EPG credentials object. + properties: + merchantId: + type: string + description: ID of the merchant. + productId: + type: string + description: ID of the product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + merchantPassword: + type: string + description: Merchant password. + format: password + writeOnly: true + merchantKey: + type: string + description: Merchant key. + format: password + writeOnly: true + required: + - merchantId + - productId + - merchantPassword + - merchantKey + EPro: + description: EPro gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: EPro credentials object. + properties: + apiSecretKey: + type: string + description: EPro secret API key. + format: password + writeOnly: true + required: + - apiSecretKey + EcorePay: + description: EcorePay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + accountId: + type: string + description: ID of the EcorePay account. + accountAuth: + type: string + description: EcorePay account authentication. + format: password + writeOnly: true + required: + - accountId + - accountAuth + Elavon: + description: Elavon gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Elavon credentials object. + properties: + ssl_merchant_id: + type: string + description: ID of the Elavon merchant. + ssl_user_id: + type: string + description: ID of the Elavon user. + ssl_pin: + type: string + description: Elavon pin. + format: password + writeOnly: true + required: + - ssl_merchant_id + - ssl_user_id + - ssl_pin + EMS: + description: EMS e-Commerce (XML) API gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + settings: + type: object + description: EMS settings object. + properties: + delay: + type: integer + description: Automatic capture delay in hours. + credentials: + type: object + description: EMS credentials object. + properties: + storeId: + type: string + description: ID of the EMS store. + userId: + type: string + description: ID of the EMS account. + password: + type: string + format: password + description: EMS password. + writeOnly: true + privateKey: + type: string + format: password + description: Private key. + x-multiline: true + writeOnly: true + privateKeyPassword: + type: string + format: password + description: Private key password. + writeOnly: true + clientCertificate: + type: string + description: Client certificate. + x-multiline: true + clientCertificatePassword: + type: string + format: password + description: Client certificate password. + writeOnly: true + serverCertificate: + type: string + description: Server certificate. + x-multiline: true + merchantName: + type: string + description: Merchant name for SFTP reconciliation. + sftpPrivateKey: + type: string + format: password + description: SFTP reconciliation private key. + x-multiline: true + writeOnly: true + required: + - storeId + - userId + - password + - privateKey + - privateKeyPassword + - clientCertificate + - clientCertificatePassword + - serverCertificate + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + ePay: + description: ePay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: ePay credentials object. + properties: + merchantId: + type: string + description: ID of the ePay merchant. + secretKey: + type: string + format: password + description: ePay secret key. + writeOnly: true + required: + - merchantId + - secretKey + Euteller: + description: Euteller gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Euteller credentials object. + properties: + username: + type: string + description: Euteller username. + password: + type: string + format: password + description: Euteller password. + writeOnly: true + required: + - username + - password + Ezeebill: + description: Ezeebill gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Ezeebill credentials object. + properties: + merchantId: + type: string + description: Merchant ID (MID) of the Ezeebill gateway. + accessId: + type: string + description: Access ID of the Ezeebill gateway. + format: password + writeOnly: true + terminalId: + type: string + description: Terminal ID of the Ezeebill gateway. + operatorId: + type: string + description: Operator ID of the Ezeebill gateway. + password: + type: string + description: API access operator password of the Ezeebill gateway. + format: password + writeOnly: true + hashKey: + type: string + description: Hash key of the Ezeebill gateway. + format: password + writeOnly: true + required: + - merchantId + - accessId + - terminalId + - operatorId + - password + - hashKey + eZeeWallet: + description: eZeeWallet gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: eZeeWallet credentials object. + properties: + apiUsername: + type: string + description: eZeeWallet API username. + format: password + writeOnly: true + apiPassword: + type: string + description: eZeeWallet API password. + format: password + writeOnly: true + required: + - apiUsername + - apiPassword + ezyEFT: + description: ezyEFT gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: ezyEFT credentials object. + properties: + merchantId: + type: string + description: ID of the ezyEFT merchant. + merchantToken: + type: string + format: password + description: ezyEFT merchant token. + writeOnly: true + required: + - merchantId + - merchantToken + FinTecSystems: + description: FinTecSystems gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + description: FinTecSystems credentials object. + properties: + apiKey: + type: string + description: FinTecSystems API key. + format: password + writeOnly: true + required: + - apiKey + settings: + type: object + description: FinTecSystems settings object. + properties: + recipientIBAN: + type: string + description: IBAN of the recipient account. + recipientBIC: + type: string + description: BIC of the recipient account. + recipientCountry: + type: string + description: Two letter country code. + enum: + - AT + - CH + - DE + recipientHolder: + type: string + description: Account holder of the recipient account. + required: + - recipientIBAN + - recipientBIC + - recipientHolder + - recipientCountry + Finrax: + description: Finrax gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Finrax credentials object. + properties: + businessId: + type: string + description: ID of the Finrax business. + apiKey: + type: string + description: Finrax API key. + format: password + writeOnly: true + apiSecret: + type: string + description: Finrax API secret. + format: password + writeOnly: true + required: + - businessId + - apiKey + - apiSecret + settings: + type: object + description: Finrax settings object. + required: + - tolerancePercentage + properties: + tolerancePercentage: + $ref: '#/components/schemas/AmountAdjustmentTolerance' + Flexepin: + description: Flexepin gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + apiKey: + type: string + description: Flexepin API key. + apiSecret: + type: string + description: Flexepin API secret. + format: password + writeOnly: true + required: + - apiKey + - apiSecret + settings: + type: object + description: Flexepin settings object. + properties: + adjustAmountFromVoucher: + type: boolean + description: >- + Specifies whether to adjust the amount or currency based on + a Flexepin voucher. + default: false + example: false + Forte: + description: Forte gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + accountId: + type: string + description: ID of the Forte account. + locationId: + type: string + description: ID of the Forte location. + apiAccessId: + type: string + description: ID of the Forte API access. + format: password + writeOnly: true + apiSecretKey: + type: string + description: Forte API secret key. + format: password + writeOnly: true + required: + - accountId + - locationId + - apiAccessId + - apiSecretKey + FundSend: + description: FundSend gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + clientId: + type: string + description: ID of the FundSend client. + secretWord: + type: string + description: FundSend secret word. + format: password + writeOnly: true + required: + - clientId + - secretWord + GET: + description: GET gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + accountId: + type: string + description: ID of the GET account. + required: + - accountId + Gigadat: + description: Gigadat gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Gigadat credentials object. + properties: + campaignId: + type: string + description: ID of the Gigadat campaign. + accessToken: + type: string + description: Gigadat access token. + format: password + writeOnly: true + securityToken: + type: string + description: Gigadat security token. + format: password + writeOnly: true + required: + - campaignId + - accessToken + - securityToken + settings: + type: object + properties: + sandbox: + type: boolean + description: Specifies if the gateway account is in sandbox mode. + default: false + required: + - sandbox + GlobalOne: + description: GlobalOne gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + terminalId: + type: string + description: ID of the GlobalOne terminal. + sharedSecret: + type: string + description: GlobalOne shared secret. + format: password + writeOnly: true + required: + - terminalId + - sharedSecret + Gooney: + description: Gooney gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Gooney credentials object. + properties: + apiUser: + type: string + description: Gooney API user. + apiPassword: + type: string + description: Gooney API password. + format: password + writeOnly: true + apiKey: + type: string + description: Gooney API key. + apiSecret: + type: string + description: Gooney API secret. + format: password + writeOnly: true + required: + - username + - password + - apiKey + - apiSecret + Gpaysafe: + description: Gpaysafe gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + apiKey: + type: string + description: Gpaysafe apiKey. + required: + - apiKey + Greenbox: + description: Greenbox gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Greenbox credentials object. + properties: + clientId: + type: string + description: ID of the Greenbox client. + locationId: + type: string + description: ID of the Greenbox location. + clientSecret: + type: string + description: Greenbox client secret. + format: password + writeOnly: true + required: + - clientId + - clientSecret + - locationId + HiPay: + description: HiPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: HiPay credentials object. + properties: + apiUsername: + type: string + description: HiPay API user name. + apiPassword: + type: string + description: HiPay API password. + format: password + writeOnly: true + required: + - apiUsername + - apiPassword + ICEPAY: + description: ICEPAY gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: ICEPAY credentials object. + properties: + merchantId: + type: string + description: ID of the ICEPAY merchant. + format: password + writeOnly: true + secretKey: + type: string + description: ICEPAY API secret key. + format: password + writeOnly: true + required: + - merchantId + - secretKey + INOVAPAY: + description: INOVAPAY gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: INOVAPAY credentials object. + properties: + apiKey: + type: string + description: INOVAPAY API key. + apiSecret: + type: string + description: INOVAPAY API secret. + format: password + writeOnly: true + required: + - apiKey + - apiSecret + Ilixium: + description: Ilixium gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Ilixium credentials object. + properties: + merchantId: + type: string + description: ID of the Ilixium merchant. + accountId: + type: string + description: ID of the Ilixium account. + digestPassword: + type: string + description: Ilixium digest password. + format: password + writeOnly: true + sftpUsername: + type: string + description: SFTP username. + sftpPrivateKey: + type: string + description: SFTP private key. + format: password + x-multiline: true + writeOnly: true + sftpKeyPassphrase: + type: string + format: password + description: Passphrase of the SFTP private key. + writeOnly: true + required: + - merchantId + - accountId + - digestPassword + settings: + type: object + description: Ilixium settings object. + properties: + useIpFrame: + type: boolean + description: >- + Specifies whether to force Ilixium to process using Ip + Frame. + useCreditEndpoint: + type: boolean + description: >- + Specifies whether to use a previous approval credit endpoint + for payouts. + useStandaloneCreditEndpoint: + type: boolean + description: >- + Specifies whether to use a standalone (token) credit + endpoint for payouts. + platform: + type: string + description: Direct API platform. + default: itix + enum: + - itix + - tpg + threeDSecureServer: + type: object + description: Ilixium 3DS server. + properties: + name: + description: Name of the merchant plug-in. + type: string + enum: + - Ilixium3dsServer + Ingenico: + description: Ingenico gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantId: + type: string + description: ID of the Ingenico merchant. + apiKeyId: + type: string + description: ID of the Ingenico API key. + apiSecretKey: + type: string + description: Ingenico API secret key. + format: password + writeOnly: true + skipFraudService: + type: boolean + description: Ingenico skip fraud service. + required: + - merchantId + - apiKeyId + - apiSecretKey + Inovio: + description: Inovio gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + description: Inovio credentials object. + properties: + username: + type: string + description: Inovio username. + password: + type: string + description: Inovio password. + format: password + writeOnly: true + required: + - username + - password + settings: + type: object + description: Inovio settings object. + properties: + websiteId: + type: string + description: ID of the Inovio website. + maxLength: 50 + example: web_0YV7DE4Z26DQSA1AC92FBJ7SEG + merchantAccountId: + type: string + description: ID of the Inovio merchant account. + productId: + type: string + description: ID of the Inovio product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + required: + - websiteId + - merchantAccountId + - productId + InstaDebit: + description: InstaDebit gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: InstaDebit credentials object. + properties: + merchantId: + type: string + description: InstaDebit merchant account number. + password: + type: string + description: InstaDebit merchant account password. + format: password + writeOnly: true + required: + - merchantId + - password + settings: + type: object + properties: + merchantSubId: + type: integer + description: Sub ID of the merchant or organization. + Intuit: + description: Intuit gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + clientId: + type: string + description: Intuit client_id key. + clientSecret: + type: string + description: Intuit client_secret key. + format: password + writeOnly: true + required: + - clientId + - clientSecret + IpayOptions: + description: Ipay Options gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + description: Ipay Options credentials object. + properties: + sid: + type: string + description: ID of the Ipay Options website. + rcode: + type: string + description: Ipay Options rcode. + format: password + writeOnly: true + required: + - sid + - rcode + settings: + type: object + description: Ipay Options settings object. + properties: + extraStep: + type: boolean + description: >- + Specifies whether to display an extra step where the user + must enter their email and DNI number. + subdomain: + type: string + description: Subdomain to use when sending request to IpayOptions. + enum: + - miglite + - w88asiapay + platform: + type: string + description: Platform which IpayOptions processes. + enum: + - SOAP + - TxHandler + - SecureHosted + cardType: + type: string + description: Manually set the card_type for iDEAL. + enum: + - ideal + - idealqr + - sofort + JetPay: + description: JetPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + TerminalID: + type: string + description: ID of the JetPay terminal. + required: + - TerminalID + Jeton: + description: Jeton gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + apiKey: + type: string + description: Jeton API key. + format: password + writeOnly: true + required: + - apiKey + settings: + type: object + properties: + method: + type: string + description: Jeton method. + enum: + - CHECKOUT + - DIRECT + - QR + - JETGO + required: + - method + JPMOrbital: + description: JPMOrbital gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantId: + type: string + description: ID of the JPMOrbital merchant. + username: + type: string + description: Username of the JPMOrbital merchant. + password: + type: string + description: Password of the JPMOrbital merchant. + format: password + writeOnly: true + required: + - merchantId + - username + - password + Khelocard: + description: Khelocard gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Credentials object. + required: + - merchantId + - referrer + - apiKey + - apiSecret + properties: + merchantId: + type: string + description: ID of the Khelocard merchant. + referrer: + type: string + description: Referrer URL registered at Khelocard. + format: uri + apiKey: + type: string + description: Khelocard API key. + format: password + writeOnly: true + apiSecret: + type: string + description: Khelocard API secret. + format: password + writeOnly: true + Klarna: + description: Klarna gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + description: Credentials object. + required: + - username + - password + properties: + username: + type: string + description: Klarna API username. + password: + type: string + description: Klarna API password. + format: password + writeOnly: true + settings: + type: object + description: Settings object. + required: + - region + properties: + region: + type: string + description: Klarna API region. + enum: + - EU + - NA + - OC + usePayNowStandalone: + type: boolean + description: >- + Specifies whether to use the Klarna Pay Now Standalone + method. + default: false + example: false + Konnektive: + description: Konnektive gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + description: Credentials object. + required: + - loginId + - password + properties: + loginId: + type: string + description: ID of the Konnektive API login. + password: + type: string + description: Konnektive API password. + format: password + writeOnly: true + settings: + type: object + description: Settings object. + required: + - campaignId + - productId + properties: + campaignId: + type: string + description: Konnektive campaign ID for which the order is being placed. + productId: + type: string + description: >- + Konnektive campaign product ID for which the order is being + placed. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + loonie: + description: loonie gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: loonie credentials object. + properties: + merchantId: + type: string + description: ID of the loonie merchant. + merchantToken: + type: string + format: password + description: loonie merchant token. + writeOnly: true + required: + - merchantId + - merchantToken + LPG: + description: LPG gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: LPG credentials object. + properties: + publicKey: + type: string + description: LPG public API key. + secureKey: + type: string + description: LPG secure API key. + format: password + writeOnly: true + payoutUsername: + type: string + description: LPG payout account username. + format: password + writeOnly: true + payoutPassword: + type: string + description: LPG payout account password. + format: password + writeOnly: true + required: + - publicKey + - secureKey + MaxiCash: + description: MaxiCash gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: MaxiCash credentials object. + properties: + merchantId: + type: string + description: ID of the MaxiCash API merchant. + password: + type: string + description: MaxiCash API password. + format: password + writeOnly: true + required: + - merchantId + - password + MercadoPago: + description: MercadoPago gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: MercadoPago credentials object. + properties: + publicKey: + type: string + description: MercadoPago public key. + accessToken: + type: string + description: MercadoPago access token. + format: password + writeOnly: true + required: + - publicKey + - accessToken + MiFinity: + description: MiFinity gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: MiFinity credentials object. + properties: + apiKey: + type: string + format: password + description: MiFinity API key. + writeOnly: true + mifinityAccountNumber: + type: string + format: password + description: MiFinity account number. + writeOnly: true + accountHolderId: + type: string + description: ID of the MiFinity account holder. + brandId: + type: string + description: >- + Three-digit ID provided by MiFinity which indicates through + which brand the call made. + required: + - apiKey + - mifinityAccountNumber + - accountHolderId + MobilePay: + description: MobilePay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + providerId: + type: string + description: ID of the MobilePay provider. + merchantVat: + type: string + description: VAT number of the MobilePay merchant. + clientId: + type: string + description: ID of the MobilePay client. + clientSecret: + type: string + description: MobilePay client secret. + format: password + writeOnly: true + required: + - providerId + - merchantVat + - clientId + - clientSecret + Moneris: + description: Moneris gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + apiToken: + type: string + description: Moneris API token. + format: password + writeOnly: true + storeId: + type: string + description: ID of the Moneris store. + required: + - storeId + - apiToken + MtaPay: + description: MTA Pay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + properties: + accountId: + type: string + description: ID of the MTA Pay account. + partyId: + type: string + description: ID of the MTA Pay party. + md5key: + type: string + description: MTA Pay MD5 key. + format: password + writeOnly: true + required: + - accountId + - partyId + - md5key + settings: + type: object + description: MTA Pay settings object. + properties: + goods: + type: string + description: MTA Pay goods. + mobilePay: + type: string + description: MTA Pay mobile pay parameter. + required: + - mobilePay + - goods + MuchBetter: + description: MuchBetter gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: MuchBetter credentials object. + properties: + merchantAccountId: + type: string + description: ID of the MuchBetter merchant account. + apiKey: + type: string + description: MuchBetter API key. + format: password + writeOnly: true + required: + - merchantAccountId + - apiKey + settings: + type: object + description: MuchBetter settings object. + properties: + brandName: + type: string + description: MuchBetter brand name used for reporting and logo. + hasPhoneNumberRequest: + type: boolean + description: >- + Specifies whether to request phone a number before + submitting the request to MuchBetter. + default: false + MuchBetterGateway: + description: MuchBetterGateway gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: MuchBetterGateway credentials object. + properties: + apiKey: + type: string + description: MuchBetterGateway API key. + format: password + writeOnly: true + secretKey: + type: string + description: MuchBetterGateway secret key. + format: password + writeOnly: true + required: + - apiKey + - secretKey + MyFatoorah: + description: MyFatoorah gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: MyFatoorah credentials object. + properties: + apiKey: + type: string + description: MyFatoorah API key. + format: password + writeOnly: true + required: + - apiKey + Neosurf: + description: Neosurf gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Neosurf credentials object. + properties: + merchantId: + type: string + description: ID of the Neosurf merchant. + secretKey: + type: string + description: Neosurf API secret key. + format: password + writeOnly: true + required: + - merchantId + - secretKey + NMI: + description: NMI gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + username: + type: string + description: NMI API token. + password: + type: string + description: ID of the NMI store. + format: password + writeOnly: true + required: + - username + - password + settings: + type: object + properties: + disableStoredCredentials: + description: >- + Specifies whether to disable NMI stored credentials for + Customer Initiated Transactions (CIT) and Merchant Initiated + Transactions (MIT). + type: boolean + default: false + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + Netbanking: + description: Netbanking gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Netbanking credentials object. + properties: + midcode: + type: string + description: Netbanking Merchant Identification (MID) code. + midsecret: + type: string + description: Netbanking Merchant Identification (MID) secret key. + format: password + writeOnly: true + required: + - midcode + - midsecret + Neteller: + description: Neteller gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Neteller credentials object. + properties: + paysafePaymentsApiUsername: + type: string + description: Neteller Paysafe Payments API username for a private key. + paysafePaymentsApiPassword: + type: string + description: Neteller Paysafe Payments API password for a private key. + format: password + writeOnly: true + required: + - paysafePaymentsApiUsername + - paysafePaymentsApiPassword + settings: + type: object + properties: + populateCustomerEmail: + type: boolean + description: Specifies whether to the populate customer email at payment. + NGenius: + description: NGenius gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: NGenius credentials object. + properties: + outletId: + type: string + description: ID of the NGenius outlet. + apiKey: + type: string + description: NGenius API key. + format: password + writeOnly: true + required: + - outletId + - apiKey + NinjaWallet: + description: NinjaWallet gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: NinjaWallet credentials object. + properties: + apiKey: + type: string + description: NinjaWallet API key. + format: password + writeOnly: true + secret: + type: string + description: NinjaWallet secret. + format: password + writeOnly: true + passphrase: + type: string + description: NinjaWallet passphrase. + format: password + writeOnly: true + required: + - apiKey + - secret + - passphrase + NordikCoin: + description: NordikCoin gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: NordikCoin credentials object. + properties: + merchantId: + type: string + description: ID of the NordikCoin merchant. + secret: + type: string + description: NordikCoin secret key. + format: password + writeOnly: true + required: + - merchantId + - secret + NOWPayments: + description: NOWPayments gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: NOWPayments credentials object. + properties: + apiKey: + type: string + format: password + writeOnly: true + ipnSecret: + type: string + format: password + writeOnly: true + required: + - apiKey + - ipnSecret + settings: + type: object + description: NOWPayments settings object. + required: + - tolerancePercentage + properties: + tolerancePercentage: + $ref: '#/components/schemas/AmountAdjustmentTolerance' + NuaPay: + description: NuaPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: NuaPay credentials object. + properties: + nuaPayCommonName: + type: string + description: NuaPay common name. + nuaPaySerialNumber: + type: string + description: NuaPay serial number. + nuaPayAccountId: + type: string + description: ID of the NuaPay account. + nuaPayOriginatorIban: + type: string + description: NuaPay origiantor IBAN. + format: password + writeOnly: true + nuaPayApiKey: + type: string + description: NuaPay API key. + format: password + writeOnly: true + nuaPayPrivateKey: + type: string + description: NuaPay private key. + format: password + x-multiline: true + writeOnly: true + required: + - nuaPayCommonName + - nuaPaySerialNumber + - nuaPayAccountId + - nuaPayOriginatorIban + - nuaPayApiKey + - nuaPayPrivateKey + OchaPay: + description: OchaPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + apiUsername: + type: string + description: OchaPay username. + apiPassword: + type: string + description: OchaPay API password. + format: password + writeOnly: true + secretWord: + type: string + description: OchaPay secret word. + format: password + writeOnly: true + required: + - apiUsername + - apiPassword + - secretWord + Onlineueberweisen: + description: Onlineueberweisen gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + description: Onlineueberweisen credentials object. + properties: + apiKey: + type: string + description: Onlineueberweisen API key. + format: password + writeOnly: true + nuaPayApiKey: + type: string + description: NuaPay API key for reconciliation. + format: password + writeOnly: true + nuaPayAccountId: + type: string + description: NuaPay account ID for reconciliation. + required: + - apiKey + settings: + type: object + description: Onlineueberweisen settings object. + properties: + payformCode: + type: string + description: Onlineueberweisen code to use a customized PayForm template. + default: default + OnRamp: + description: OnRamp gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: OnRamp credentials object. + properties: + apiKey: + type: string + format: password + writeOnly: true + required: + - apiKey + settings: + type: object + description: OnRamp settings object. + properties: + useServerToServerApi: + type: boolean + description: Specifies whether to use OnRamp server to server API. + default: false + logoUrl: + type: string + description: Set this value to use a custom logo. + format: url + Orbital: + description: Orbital gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Orbital credentials object. + properties: + apiKey: + type: string + description: Orbital API key. + format: password + writeOnly: true + required: + - apiKey + settings: + type: object + description: Orbital settings object. + properties: + targetCurrency: + description: Currency to which the received cryptocurrency is converted. + type: string + enum: + - USD + - EUR + - GBP + mainCryptoCurrency: + description: Blockchain network to be used. + type: string + enum: + - ETH + - SOL + - TRX + logoImageUrl: + description: >- + Logo image in PNG or SVG format to be displayed on the + deposit screen. + type: string + productImageUrl: + description: >- + Product image in JPEG format to be displayed on the deposit + screen. + type: string + Pagadito: + description: Pagadito gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Pagadito credentials object. + properties: + uid: + type: string + format: password + description: Pagadito UID. + writeOnly: true + wsk: + type: string + format: password + description: Pagadito WSK. + writeOnly: true + required: + - uid + - wsk + settings: + type: object + properties: + useRecurringApi: + type: boolean + default: false + description: >- + Specifies whether to use the Pagadito API for reoccurring + payments. + Pagsmile: + description: Pagsmile gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantId: + type: string + description: ID of the Pagsmile merchant. + appId: + type: string + description: ID of the Pagsmile application. + secretKey: + type: string + description: Pagsmile MD5 key. + format: password + writeOnly: true + apiSecretKey: + type: string + description: >- + Pagsmile secret key for the Trade Pay API. The key starts + with `Pagsmile_sk`. + format: password + writeOnly: true + required: + - merchantId + - appId + - secretKey + settings: + type: object + description: Pagsmile settings object. + properties: + useTradePay: + type: boolean + description: Specifies whether to use the Trade Pay API. + default: false + example: false + Panamerican: + description: Panamerican gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + description: Panamerican credentials object. + properties: + key: + type: string + description: Panamerican key. + password: + type: string + description: Panamerican password. + format: password + writeOnly: true + required: + - key + - password + settings: + type: object + description: Panamerican settings object. + properties: + extraStep: + type: boolean + description: >- + Specifies whether to display a step where the user must + enter their ID number. + convertToAscii: + type: boolean + description: Specifies whether to convert all chars to ascii. + required: + - extraStep + PandaGateway: + description: Panda gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantCode: + type: string + description: Panda merchant code. + apiCode: + type: string + description: Panda API code. + signKey: + type: string + description: Panda sign key. + format: password + writeOnly: true + required: + - merchantCode + - apiCode + - signKey + ParamountCommerce: + description: ParamountCommerce gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: ParamountCommerce credentials object. + properties: + merchantId: + type: string + description: ParamountCommerce merchant ID. + appId: + type: string + description: ParamountCommerce application ID. + clientId: + type: string + description: ParamountCommerce client ID. + clientSecret: + type: string + description: ParamountCommerce client secret. + format: password + writeOnly: true + required: + - merchantId + - appId + - clientId + - clientSecret + settings: + type: object + properties: + merchantSubId: + type: string + description: ParamountCommerce merchant sub ID. + merchantTransactionType: + type: string + description: ParamountCommerce merchant transaction type. + enum: + - POKER + - CASINO + - BINGO + - SPORTS_BETTING + - DIGITAL_REMITTANCE + - E_SPORTS + useDirectDeposit: + type: boolean + description: Specifies whether to use One-Click Payments. + default: false + example: false + required: + - merchantSubId + - merchantTransactionType + ParamountEft: + description: Paramount gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Paramount credentials object. + properties: + merchant_id: + type: string + description: ID of the merchant account. + merchant_pass: + type: string + description: Merchant account password. + format: password + writeOnly: true + payee: + type: string + description: Merchant name or descriptor. + required: + - merchant_id + - merchant_pass + - payee + ParamountInterac: + description: ParamountInterac gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: ParamountInterac credentials object. + properties: + merchantId: + type: string + description: ID of the ParamountInterac merchant. + encryptionKey: + type: string + description: ParamountInterac encryption key. + format: password + writeOnly: true + password: + type: string + description: ParamountInterac password for query. + format: password + writeOnly: true + required: + - merchantId + - encryptionKey + - password + settings: + type: object + properties: + sandbox: + type: boolean + description: Specifies if the gateway account is in sandbox mode. + default: false + merchantSubId: + type: integer + description: Sub ID of the merchant or organization. + Pay4Fun: + description: Pay4Fun gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Pay4Fun credentials object. + properties: + merchantId: + type: string + description: ID of the Pay4Fun merchant. + merchantSecret: + type: string + description: Pay4Fun merchant secret. + format: password + writeOnly: true + merchantKey: + type: string + description: Pay4Fun merchant key. + format: password + writeOnly: true + required: + - merchantId + - merchantSecret + - merchantKey + PayCash: + description: PayCash gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: PayCash credentials object. + properties: + emisor: + type: string + description: PayCash merchant identifier. + token: + type: string + description: PayCash token. + format: password + writeOnly: true + required: + - emisor + - token + PayClub: + description: PayClub gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: PayClub credentials object. + properties: + merchantId: + type: string + description: ID of the PayClub merchant. + accountNumber: + type: string + description: PayClub account number. + signKey: + type: string + format: password + description: PayClub sign key. + writeOnly: true + required: + - apiKey + - accountNumber + - signKey + settings: + type: object + properties: + use3DSChannel: + type: boolean + description: Specifies whether to use a 3DS channel. + PayEcards: + description: PayEcards gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: PayEcards credentials object. + properties: + clientKey: + type: string + description: Client key of the PayEcards gateway. + password: + type: string + description: Password of the PayEcards gateway. + format: password + writeOnly: true + paymentUrl: + type: string + description: Payment URL of the PayEcards gateway for S2S integration. + required: + - clientKey + - password + - paymentUrl + Paynote: + description: Paynote gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + publicKey: + type: string + description: Paynote public key. + secretKey: + type: string + format: password + description: Paynote secret key. + writeOnly: true + required: + - publicKey + - secretKey + PayPal: + description: PayPal gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - settings + properties: + credentials: + type: object + description: >- + PayPal credentials object. + + This object specifies the PayPal Partner account from which a + gateway account is registered. + properties: + partnerId: + type: string + description: ID of the PayPal Partner account. + partnerClientId: + type: string + description: ID of the PayPal Partner account API client. + partnerSecret: + type: string + description: Secret key for the PayPal Partner account API client. + format: password + writeOnly: true + partnerBnCode: + type: string + description: |- + BN Code for the PayPal Partner account. + This unique code tracks the source of API requests. + settings: + type: object + description: PayPal settings object. + required: + - redirectUrl + properties: + redirectUrl: + type: string + description: >- + URL where the user is redirected after authorizing the + account on PayPal. + format: url + enableGuestCheckout: + type: boolean + default: false + description: >- + Specifies whether to allow users without PayPal accounts to + pay using credit or debit cards. + useHostedCheckoutForm: + type: boolean + default: false + description: >- + Specifies whether to use a Rebilly hosted PayPal form + instead of redirecting customers to PayPal. + + For more information, see [Create a payment form using the + Rebilly + API](https://www.rebilly.com/docs/dev-docs/create-a-hosted-payment-form/). + forceGuestCheckout: + type: boolean + default: false + description: >- + Specifies whether to suppress the PayPal payment method when + a guest checkout is available. + + This option is only applicable when `useHostedCheckoutForm` + is enabled. + enableAlternativePaymentMethods: + type: boolean + default: false + description: >- + Specifies whether to allow customers to pay using the + alternative payment options supported by PayPal. + + When enabled, customers must manually approve all payments. + Payeezy: + description: Payeezy gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantId: + type: string + description: ID of the Payeezy merchant. + merchantToken: + type: string + description: Merchant token. + format: password + writeOnly: true + apiKey: + type: string + description: API key. + apiSecret: + type: string + description: API secret. + format: password + writeOnly: true + required: + - merchantId + - merchantToken + - apiKey + - apiSecret + Payflow: + description: Payflow gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Payflow credentials object. + properties: + user: + type: string + description: >- + Payflow user. + + If you set up one or more additional users on the account, + + this value is the ID of the user that is authorized to + process transactions. + + If do not set up additional users on the account, + + `user` has the same value as `vendor`. + vendor: + type: string + description: ID of the Payflow merchant login. + password: + type: string + description: Payflow merchant password. + format: password + writeOnly: true + required: + - user + - vendor + - password + PaymentAsia: + description: PaymentAsia gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: PaymentAsia credentials object. + properties: + apiKey: + type: string + format: password + description: PaymentAsia API key. + writeOnly: true + required: + - apiKey + PaymenTechnologies: + description: PaymenTechnologies gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + authenticateId: + type: string + description: ID of the PaymenTechnologies authenticate. + authenticatePw: + type: string + description: PaymenTechnologies authenticate password. + format: password + writeOnly: true + publicKey: + type: string + description: PaymenTechnologies API public key. + secretKey: + type: string + description: PaymenTechnologies API secret key. + format: password + writeOnly: true + apiKey: + type: string + description: PaymenTechnologies API key required for query operations. + format: password + writeOnly: true + required: + - authenticateId + - authenticatePw + - publicKey + - secretKey + - apiKey + settings: + type: object + properties: + use3DSEndpoint: + type: boolean + description: Specifies whether to use a 3DS endpoint. + PaymentsOS: + description: PaymentsOS gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + appId: + type: string + description: ID of the PaymentsOS application. + publicKey: + type: string + description: PaymentsOS public key. + privateKey: + type: string + format: password + description: PaymentsOS private key. + writeOnly: true + required: + - appId + - privateKey + - publicKey + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + Paymero: + description: Paymero gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Paymero credentials object. + properties: + apiKey: + type: string + description: Paymero API key. + format: password + writeOnly: true + required: + - apiKey + settings: + type: object + description: Paymero settings object. + properties: + targetCurrency: + description: Currency to which the received cryptocurrency is converted. + type: string + minLength: 3 + maxLength: 3 + example: USD + mainCurrency: + description: Blockchain on which the currency is processed. + type: string + enum: + - TRX + - ETH + amountExceeded: + description: >- + Specifies whether to decline transactions when the amount + received exceeds the amount requested. + type: boolean + default: false + Payper: + description: Payper gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Payper credentials object. + required: + - secretKey + properties: + secretKey: + type: string + format: password + description: Payper secret key. + writeOnly: true + Payr: + description: Payr gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + clientId: + type: string + description: ID of the Payr client. + secretWord: + type: string + description: Payr secret word. + format: password + writeOnly: true + apiUserId: + type: string + description: |- + Username for the Alliance API. + This API is used for transaction reporting. + apiSecurityToken: + type: string + description: |- + Hash of the password for the Alliance API. + This API is used for transaction reporting. + format: password + writeOnly: true + required: + - clientId + - secretWord + PayRedeem: + description: PayRedeem gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: PayRedeem credentials object. + properties: + apiUser: + type: string + description: PayRedeem API user. + apiPassword: + type: string + description: PayRedeem API password. + format: password + writeOnly: true + apiKey: + type: string + description: PayRedeem API key. + format: password + writeOnly: true + required: + - apiUser + - apiPassword + - apiKey + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + PayRetailers: + description: PayRetailers gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: PayRetailers credentials object. + properties: + shopId: + type: string + description: ID of the PayRetailers shop. + secretKey: + type: string + description: Secret key value of the PayRetailers API. + format: password + writeOnly: true + required: + - shopId + - secretKey + Paysafe: + description: Paysafe gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + storeId: + type: string + description: ID of the Paysafe store. + storePwd: + type: string + description: Paysafe store password. + format: password + writeOnly: true + accountNum: + type: string + description: Paysafe account number. + required: + - storeId + - storePwd + - accountNum + Paysafecard: + description: Paysafecard gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + apiKey: + type: string + description: Paysafecard API key. + format: password + writeOnly: true + required: + - apiKey + Paysafecash: + description: Paysafecash gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + apiKey: + type: string + description: Paysafecash API key. + format: password + writeOnly: true + required: + - apiKey + PayTabs: + description: PayTabs gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: PayTabs credentials object. + properties: + profileId: + type: string + description: ID of the PayTabs profile. + clientKey: + type: string + description: PayTabs client key. + serverKey: + type: string + format: password + description: PayTabs server key. + writeOnly: true + required: + - profileId + - clientKey + - serverKey + PayULatam: + description: PayULatam gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: PayULatam credentials object. + properties: + merchantId: + type: string + description: ID of the PayULatam merchant. + accountId: + type: string + description: ID of the PayULatam account. + apiLogin: + type: string + description: PayULatam API login. + apiKey: + type: string + description: PayULatam API key. + format: password + writeOnly: true + required: + - merchantId + - accountId + - apiLogin + - apiKey + Payvision: + description: Payvision gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + memberId: + type: string + description: ID of the Payvision member. + memberGuid: + type: string + description: Payvision member guid. + format: password + writeOnly: true + required: + - memberId + - memberGuid + settings: + type: object + description: Payvision settings object. + properties: + avs: + type: boolean + description: Specifies whether to use Payvision AVS. + delay: + type: integer + description: Automatic capture delay in hours. + merchantAccountType: + type: integer + description: Payvision merchant account type. + required: + - merchantAccountType + PharosPayments: + description: PharosPayments gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: PharosPayments credentials object. + properties: + username: + type: string + description: Authentication username. + password: + description: Authentication password. + type: string + format: password + merchantCode: + type: string + description: Merchant code. + terminalCode: + type: string + description: Terminal code. + required: + - username + - password + - merchantCode + - terminalCode + Piastrix: + description: Piastrix gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Piastrix credentials object. + properties: + shopId: + type: string + description: ID of the Piastrix shop. + secretKey: + type: string + description: Piastrix secret key. + format: password + writeOnly: true + required: + - shopId + - secretKey + settings: + type: object + description: Piastrix settings object. + properties: + tolerancePercentage: + type: integer + description: Tolerance percentage for settled amounts. + minimum: 0 + maximum: 5 + example: 5 + Pin4Pay: + description: Pin4Pay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Credentials object. + required: + - username + - requestOrigin + - codigoCliente + - keyValue + properties: + username: + type: string + description: Pin4Pay API username. + requestOrigin: + type: string + description: Pin4Pay API request origin. + codigoCliente: + type: string + description: Pin4Pay API codigo cliente. + keyValue: + type: string + description: Pin4Pay API password. + format: password + writeOnly: true + Plugnpay: + description: Plugnpay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + publisher-name: + type: string + description: ID of the Plugnpay member. + publisher-password: + type: string + description: Plugnpay AVS. + format: password + writeOnly: true + required: + - publisher-name + - publisher-password + PostFinance: + description: PostFinance gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: PostFinance credentials object. + properties: + pspId: + type: string + description: ID of the PostFinance PSP. + merchantId: + type: string + description: ID of the PostFinance merchant. + sftpUsername: + type: string + description: PostFinance SFTP username. + publicKey: + type: string + x-multiline: true + description: PostFinance public key. + privateKey: + type: string + format: password + x-multiline: true + description: PostFinance private key. + writeOnly: true + keyPassphrase: + type: string + format: password + description: PostFinance key passphrase. + writeOnly: true + sftpPrivateKey: + type: string + format: password + x-multiline: true + description: PostFinance SFTP private key. + writeOnly: true + sftpKeyPassphrase: + type: string + format: password + description: PostFinance SFTP key passphrase. + writeOnly: true + required: + - pspId + - merchantId + - sftpUsername + - publicKey + - privateKey + - keyPassphrase + - sftpPrivateKey + - sftpKeyPassphrase + settings: + type: object + properties: + skipPaymentFileUpload: + type: boolean + default: false + description: Specifies whether to skip the payment file upload screen. + enableAliasAuthentication: + type: boolean + default: false + description: Specifies whether to enable alias authentication. + PSiGate: + description: PSiGate gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + username: + type: string + description: Username or store name. + password: + type: string + description: Password of the PSiGate gateway account. + format: password + writeOnly: true + clientId: + type: string + description: Client ID. + clientApiKey: + type: string + description: Client API key. + format: password + writeOnly: true + disputeUsername: + type: string + description: Username for fetching disputes. + disputePassword: + type: string + description: Password for fetching disputes. + format: password + writeOnly: true + required: + - username + - password + - clientId + - clientApiKey + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + PPRO: + description: PPRO gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: PPRO credentials object. + properties: + login: + type: string + description: PPRO merchant API username. + password: + type: string + description: PPRO merchant API password. + format: password + writeOnly: true + contractId: + type: string + description: |- + ID of the PPRO contract. + This value is used for API credentials. + privateKey: + type: string + format: password + description: PPRO private key. + x-multiline: true + writeOnly: true + privateKeyPassword: + type: string + format: password + description: PPRO private key password. + writeOnly: true + clientCertificate: + type: string + description: Public key. + x-multiline: true + serverCertificate: + type: string + description: PPRO CA certificate. + x-multiline: true + notificationSecret: + type: string + format: password + description: Secret used for IPN URLs. + writeOnly: true + sharedSecret: + type: string + format: password + description: Secret used for redirect URLs. + writeOnly: true + required: + - login + - password + - contractId + - privateKey + - privateKeyPassword + - clientCertificate + - serverCertificate + - notificationSecret + - sharedSecret + Prosa: + description: Prosa gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Prosa credentials object. + properties: + entityId: + type: string + description: ID of the Prosa entity. + accessToken: + type: string + format: password + description: Prosa access token. + writeOnly: true + required: + - entityId + - accessToken + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + Rapyd: + description: Rapyd gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Rapyd credentials object. + properties: + accessKey: + type: string + description: Rapyd access key. + secretKey: + type: string + format: password + description: Rapyd secret key. + writeOnly: true + required: + - accessKey + - secretKey + settings: + type: object + description: Rapyd settings object. + properties: + ipnUrl: + type: string + description: |- + Instant Payment Notification (IPN) URL configured on Rapyd. + This value is used to calculate the signature. + RPN: + description: RPN gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + mid: + type: string + description: RPN merchant identifier. + key: + type: string + description: RPN key. + format: password + writeOnly: true + required: + - mid + - key + Realex: + description: Realex gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantId: + type: string + description: ID of the Realex merchant. + secretKey: + type: string + description: Realex secret key. + format: password + writeOnly: true + rebatePassword: + type: string + description: Realex rebate password. + format: password + writeOnly: true + account: + type: string + description: Realex account. + required: + - merchantId + - secretKey + - rebatePassword + - account + Realtime: + description: Realtime gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + clientId: + type: string + description: ID of the Realtime client. + secretWord: + type: string + description: Realtime secret word. + format: password + writeOnly: true + required: + - clientId + - secretWord + Redsys: + description: Redsys gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantCode: + type: string + description: Redsys merchant code. + format: password + writeOnly: true + secretCode: + type: string + description: Redsys secret code. + format: password + writeOnly: true + required: + - merchantCode + - secretCode + settings: + type: object + description: Redsys settings object. + properties: + terminal: + type: string + description: Redsys terminal. + Rotessa: + description: Rotessa gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + description: Rotessa credentials object. + properties: + apiKey: + type: string + description: Rotessa API key. + format: password + writeOnly: true + required: + - apiKey + settings: + type: object + description: Rotessa settings object. + properties: + delay: + type: integer + description: Automatic capture delay. + required: + - delay + Safecharge: + description: Safecharge gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Safecharge credentials object. + properties: + merchantId: + type: string + description: ID of the Safecharge merchant. + siteId: + type: string + description: ID of the Safecharge site. + secretKey: + type: string + description: Safecharge secret key. + format: password + writeOnly: true + required: + - merchantId + - secretKey + - siteId + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + SaltarPay: + description: SaltarPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: SaltarPay credentials object. + properties: + apiKey: + type: string + description: SaltarPay API key. + format: password + writeOnly: true + secretKey: + type: string + description: SaltarPay secret key. + format: password + writeOnly: true + required: + - apiKey + - secretKey + SMSVoucher: + description: SMSVoucher gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + AppId: + type: string + description: ID of the SMSVoucher application. + required: + - AppId + Sofort: + description: Sofort gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Sofort credentials object. + properties: + configKey: + type: string + description: Sofort configuration key. + format: password + writeOnly: true + nuaPayApiKey: + type: string + description: NuaPay API key for reconciliation. + format: password + writeOnly: true + nuaPayAccountId: + type: string + description: NuaPay account ID for reconciliation. + required: + - configKey + Sagepay: + description: Sagepay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + M_ID: + type: string + description: ID of the Sagepay merchant. + M_KEY: + type: string + description: Sagepay merchant key. + format: password + writeOnly: true + required: + - M_ID + - M_KEY + SeamlessChex: + description: SeamlessChex gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: SeamlessChex credentials object. + properties: + publicKey: + type: string + description: SeamlessChex publishable key. + secretKey: + type: string + description: SeamlessChex secret key. + format: password + writeOnly: true + required: + - publicKey + - secretKey + SecureTrading: + description: SecureTrading gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: SecureTrading credentials object. + properties: + websiteId: + type: string + description: ID of the SecureTrading website. + maxLength: 50 + example: web_0YV7DE4Z26DQSA1AC92FBJ7SEG + username: + type: string + description: SecureTrading web service username. + password: + type: string + description: SecureTrading web service password. + format: password + writeOnly: true + notificationPassword: + type: string + description: |- + SecureTrading notification password. + This password is used to authenticate webhook requests. + format: password + writeOnly: true + required: + - websiteId + - username + - password + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + SecurionPay: + description: SecurionPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: SecurionPay credentials object. + properties: + secretApiKey: + type: string + format: password + description: SecurionPay secret API key. + writeOnly: true + required: + - secretApiKey + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + Skrill: + description: Skrill gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Skrill credentials object. + properties: + accountEmail: + type: string + description: Email address of your Skrill merchant account. + secretWord: + type: string + description: Secret word used for MD5 signature verifications. + format: password + writeOnly: true + mqiPassword: + type: string + description: Password used during MQI or API requests. + format: password + writeOnly: true + required: + - accountEmail + - secretWord + settings: + type: object + properties: + merchantFields: + type: string + description: |- + Comma-separated list of name-value merchant field pairs. + Example: `key1:value1,key2:value2`. + useSPX: + type: boolean + description: Specifies whether to use SPX code for PIX payment method. + default: false + SmartInvoice: + description: SmartInvoice gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: SmartInvoice credentials object. + properties: + merchantId: + type: string + description: SmartInvoice merchant account UID. + applicationId: + type: string + description: SmartInvoice application UID. + format: password + writeOnly: true + required: + - merchantId + - applicationId + SparkPay: + description: SparkPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: SparkPay credentials object. + properties: + shopId: + type: string + description: ID of the SparkPay shop. + secretKey: + type: string + description: SparkPay secret key. + format: password + writeOnly: true + required: + - shopId + - secretKey + StaticGateway: + description: StaticGateway gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + STPMexicoBanks: + type: integer + enum: + - 37006 + - 37009 + - 37019 + - 37135 + - 37166 + - 37168 + - 40002 + - 40012 + - 40014 + - 40021 + - 40030 + - 40036 + - 40037 + - 40042 + - 40044 + - 40058 + - 40059 + - 40060 + - 40062 + - 40072 + - 40102 + - 40103 + - 40106 + - 40108 + - 40110 + - 40112 + - 40113 + - 40124 + - 40126 + - 40127 + - 40128 + - 40129 + - 40130 + - 40131 + - 40132 + - 40133 + - 40136 + - 40137 + - 40138 + - 40140 + - 40141 + - 40143 + - 40145 + - 40147 + - 40148 + - 40150 + - 40151 + - 40152 + - 40154 + - 40155 + - 40156 + - 40158 + - 90600 + - 90601 + - 90602 + - 90605 + - 90606 + - 90608 + - 90613 + - 90614 + - 90616 + - 90617 + - 90620 + - 90621 + - 90623 + - 90626 + - 90627 + - 90628 + - 90630 + - 90631 + - 90634 + - 90636 + - 90637 + - 90638 + - 90640 + - 90642 + - 90646 + - 90648 + - 90649 + - 90652 + - 90653 + - 90655 + - 90656 + - 90659 + - 90670 + - 90671 + - 90674 + - 90677 + - 90678 + - 90679 + - 90680 + - 90681 + - 90683 + - 90684 + - 90685 + - 90686 + - 90687 + - 90689 + - 90901 + - 90902 + STPMexico: + description: STP Mexico gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: STP Mexico credentials object. + properties: + companyName: + type: string + description: STP Mexico account company name. + beneficiaryName: + type: string + description: Name of the bank account holder that is receiving payments. + bankId: + description: >- + ID of the bank receiving payments. + + A list of bank IDs can be found in STP Mexico integration + documentation. + $ref: '#/components/schemas/STPMexicoBanks' + bankAccountNumber: + type: string + description: Bank account number of the payments recipient. + privateKey: + type: string + description: STP Mexico private key. + format: password + x-multiline: true + writeOnly: true + keyPassphrase: + type: string + description: Passphrase for the private key. + format: password + writeOnly: true + required: + - companyName + - beneficiaryName + - bankId + - bankAccountNumber + - privateKey + - keyPassphrase + Stripe: + description: Stripe gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - settings + properties: + credentials: + type: object + description: >- + Stripe Connect credentials object. + + The credentials object may be provided to control the Stripe + Connect account to which the gateway account is connected. + properties: + stripeClientId: + type: string + description: ID of the Stripe account client. + stripeSecret: + type: string + description: Stripe Connect account secret key account. + format: password + writeOnly: true + settings: + type: object + description: Stripe settings object. + required: + - redirectUrl + properties: + redirectUrl: + type: string + description: >- + URL where the user is redirected after authorizing the + account on Stripe. + format: url + disablePaymentIntents: + type: boolean + description: >- + Specifies whether to use the Charges API instead of the + PaymentIntents API. + default: false + enforceOffSession: + type: boolean + description: >- + Specifies if the `off_session` parameter is set to `true` in + Stripe requests. + default: false + copyCredentialsFrom: + type: string + description: >- + ID of an existing Stripe gateway account from which + credentials are copied in order to skip the onboarding + process. + setupFutureUsage: + type: string + description: >- + Indicates intent to make future payments with this payment + method. + + - `on_session`: Reuse the payment method when the customer + is present. + + - `off_session`: Reuse the payment method when the customer + may or may not be present. + enum: + - off_session + - on_session + threeDSecureServer: + type: object + required: + - name + description: Stripe Integrated. + properties: + name: + type: string + description: Name of the Stripe 3DS server. + enum: + - Stripe3dsServer + enforceThreeDSecure: + type: boolean + description: >- + Specifies if Stripe must perform 3D Secure. This option + overrides any [dynamic 3D Secure Radar + rules](https://stripe.com/docs/radar/rules#request-3ds). + Telr: + description: Telr gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Telr credentials object. + properties: + storeId: + type: string + description: Hosted payment page V2 store identifier. + hostedPageAuthenticationKey: + type: string + description: Hosted payment page V2 authentication key. + format: password + writeOnly: true + merchantId: + type: string + description: Service API merchant identifier. + serviceApiKey: + type: string + description: Service API key. + format: password + writeOnly: true + remoteApiKey: + type: string + description: Remote API key. + format: password + writeOnly: true + required: + - storeId + - hostedPageAuthenticationKey + - merchantId + - serviceApiKey + settings: + type: object + description: Telr settings object. + properties: + transactionDescription: + type: string + description: |- + Default transaction description. + Leave this field blank to use website URL. + TestProcessor3dsServer: + type: object + description: TestProcessor Integrated. + required: + - name + properties: + name: + type: string + description: Name of the TestProcessor plug-in. + enum: + - TestSandbox3dsServer + TestProcessor3dsServers: + description: TestProcessor3dsServers 3DS servers. + discriminator: + propertyName: name + mapping: + TestSandbox3dsServer: '#/components/schemas/TestProcessor3dsServer' + ThreeDSecureIO3dsServer: '#/components/schemas/ThreeDSecureIO3dsServer' + anyOf: + - $ref: '#/components/schemas/TestProcessor3dsServer' + - $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + TestProcessor: + description: TestProcessor gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + properties: + threeDSecureServer: + $ref: '#/components/schemas/TestProcessor3dsServers' + ToditoCash: + description: ToditoCash gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: ToditoCash credentials object. + properties: + apiKey: + type: string + description: ToditoCash API key. + format: password + writeOnly: true + required: + - apiKey + Truevo: + description: Truevo gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Truevo credentials object. + properties: + mid: + type: string + description: Truevo merchant identifier. + tid: + type: string + description: Truevo terminal identifier. + token: + type: string + description: Truevo authentication token. + format: password + writeOnly: true + merchantName: + type: string + description: Merchant name for SFTP reconciliation. + sftpUsername: + type: string + description: SFTP reconciliation username. + sftpPrivateKey: + type: string + format: password + description: SFTP reconciliation private key. + x-multiline: true + writeOnly: true + sftpKeyPassphrase: + type: string + format: password + description: Passphrase of the SFTP reconciliation private key. + writeOnly: true + required: + - mid + - tid + - token + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + TrustsPay: + description: TrustsPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: TrustsPay credentials object. + required: + - merchantNo + - gatewayNo + - signkey + properties: + merchantNo: + type: string + description: TrustsPay merchant number. + gatewayNo: + type: string + description: TrustsPay gateway number. + signkey: + type: string + format: password + description: TrustsPay sign key. + writeOnly: true + Trustly: + description: Trustly gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Trustly credentials object. + properties: + username: + type: string + description: Trustly username. + password: + type: string + description: Trustly password. + format: password + writeOnly: true + publicKey: + type: string + description: Trustly public key. + x-multiline: true + privateKey: + type: string + description: Trustly private key. + format: password + x-multiline: true + writeOnly: true + required: + - usernmae + - password + - publicKey + - privateKey + TWINT: + description: TWINT gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + credentials: + type: object + description: TWINT credentials object. + properties: + storeUuid: + type: string + description: TWINT store UUID. + cashRegisterId: + type: string + description: ID of the cash register. + publicKey: + type: string + description: TWINT public key. + x-multiline: true + privateKey: + type: string + description: TWINT private key. + format: password + x-multiline: true + writeOnly: true + keyPassphrase: + type: string + description: TWINT key passphrase. + format: password + writeOnly: true + required: + - storeUuid + - cashRegisterId + - publicKey + - privateKey + - keyPassphrase + settings: + type: object + description: TWINT settings object. + properties: + cashRegisterType: + type: string + description: TWINT cash register type. + required: + - cashRegisterType + UPayCard: + description: UPayCard gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + receiver_account: + type: string + description: UPayCard merchant receiver account. + key: + type: string + description: UPayCard merchant key. + format: password + writeOnly: true + secret: + type: string + description: UPayCard merchant secret. + format: password + writeOnly: true + required: + - receiver_account + - key + - secret + settings: + type: object + description: UpayCard settings object. + required: + - tolerancePercentage + properties: + tolerancePercentage: + $ref: '#/components/schemas/AmountAdjustmentTolerance' + USAePay: + description: USAePay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + sourceKey: + type: string + description: USAePay source key. + format: password + writeOnly: true + pin: + type: string + description: USAePay pin. + format: password + writeOnly: true + required: + - sourceKey + - pin + VantivLitle: + description: VantivLitle gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + username: + type: string + description: VantivLitle username. + password: + type: string + description: VantivLitle password. + format: password + writeOnly: true + merchantId: + type: string + description: ID of the VantivLitle merchant. + required: + - username + - password + - merchantId + VCreditos: + description: VCreditos gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: VCreditos credentials object. + properties: + publicKey: + type: string + description: VCreditos API public key. + secretKey: + type: string + description: VCreditos API secret key. + format: password + writeOnly: true + required: + - publicKey + - secretKey + settings: + type: object + description: VCreditos settings object. + properties: + merchantWebsiteLoginLabel: + type: string + description: Label caption for a merchant website login input. + merchantWebsiteLoginDescription: + type: string + description: Description block for a merchant website login input. + VegaWallet: + description: VegaWallet gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: VegaWallet credentials object. + properties: + merchantId: + type: string + description: ID of the VegaWallet merchant. + password: + type: string + description: VegaWallet merchant password. + format: password + writeOnly: true + required: + - merchantId + - password + Wallet88: + description: Wallet88 gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Wallet88 credentials object. + properties: + sid: + type: string + description: ID of the Wallet88 API site. + rcode: + type: string + description: Wallet88 API rcode. + format: password + writeOnly: true + required: + - sid + - rcode + settings: + type: object + description: Wallet88 settings object. + properties: + paymentCardMethod: + description: Method to send for payment card. + type: string + enum: + - creditcard + - mastercard + default: creditcard + Walpay: + description: Walpay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantName: + type: string + description: Walpay merchant name. + merchantPin: + type: string + description: Walpay merchant pin. + format: password + writeOnly: true + required: + - merchantName + - merchantPin + WesternUnion: + description: WesternUnion gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: WesternUnion credentials object. + properties: + clientId: + type: string + description: ID of the WesternUnion client. + certificateCommonName: + type: string + description: WesternUnion certificate Common Name (CN). + certificateSerialNumber: + type: string + description: WesternUnion certificate Serial Number (SN). + required: + - clientId + - certificateCommonName + - certificateSerialNumber + Wirecard: + description: Wirecard gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantUsername: + type: string + description: Wirecard merchant username. + merchantPassword: + type: string + description: Wirecard merchant password. + format: password + writeOnly: true + businessSignature: + type: string + description: Wirecard merchant business case signature. + format: password + writeOnly: true + delay: + type: integer + description: Automatic capture delay. + sftpUsername: + type: string + description: Wirecard SFTP username. + sftpPrivateKey: + type: string + description: Wirecard SFTP private key. + format: password + x-multiline: true + writeOnly: true + required: + - merchantUsername + - merchantPassword + - businessSignature + - delay + WorldlineAtosFrankfurt: + description: WorldlineAtosFrankfurt gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + - settings + properties: + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + credentials: + type: object + description: WorldlineAtosFrankfurt credentials. + properties: + cardAcceptorIdCode: + type: string + description: Card acceptor ID Code. + acquiringInstitutionIdentificationCode: + type: string + description: Identification code of the acquiring institution. + required: + - cardAcceptorIdCode + - acquiringInstitutionIdentificationCode + settings: + type: object + description: WorldlineAtosFrankfurt settings. + properties: + cardAcceptorName: + type: string + description: Name of the card acceptor. + cardAcceptorLocation: + type: string + description: Location of the card acceptor. + cardAcceptorCountryCode: + type: string + description: Country code of the card acceptor. + terminalIds: + type: string + description: Comma-separated list of terminal IDs. + required: + - cardAcceptorName + - cardAcceptorLocation + - cardAcceptorCountryCode + Worldpay: + description: Worldpay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + merchantCode: + type: string + description: Worldpay merchant code. + format: password + writeOnly: true + merchantPassword: + type: string + description: Worldpay merchant password. + format: password + writeOnly: true + payoutMerchantCode: + type: string + description: Optional alternate merchant code for payouts. + format: password + writeOnly: true + payoutMerchantPassword: + type: string + description: Optional alternate merchant password for payouts. + format: password + writeOnly: true + alternativePaymentsUsername: + type: string + description: Optional API username for an alternative payments platform. + alternativePaymentsPassword: + type: string + description: Optional API password for an alternative payments platform. + format: password + writeOnly: true + sftpUsername: + type: string + description: SFTP username. + sftpPrivateKey: + type: string + description: SFTP private key. + format: password + x-multiline: true + writeOnly: true + sftpKeyPassphrase: + type: string + format: password + description: Passphrase of the SFTP private key. + writeOnly: true + required: + - merchantCode + - merchantPassword + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + settings: + type: object + properties: + delay: + type: integer + description: Automatic capture delay in hours. + minimum: 0 + default: 0 + enableStoredCredentials: + type: boolean + description: Specifies whether to enable stored credentials. + default: false + merchantInitiatedReason: + type: string + description: Value to send with merchant-initiated transactions. + enum: + - UNSCHEDULED + - RECURRING + - INSTALMENT + - REAUTH + - DELAYED + - INCREMENTAL + - RESUBMISSION + - NOSHOW + default: UNSCHEDULED + Zotapay: + description: Zotapay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + endpointId: + type: string + description: ID of the Zotapay endpoint. + merchantId: + type: string + description: ID of the Zotapay merchant. + merchantSecretKey: + type: string + description: Zotapay merchant secret key. + format: password + writeOnly: true + required: + - endpointId + - merchantId + - merchantSecretKey + eMerchantPay: + description: eMerchantPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + clientId: + type: string + description: ID of the eMerchantPay client. + apiKey: + type: string + description: eMerchantPay API key. + format: password + writeOnly: true + username: + type: string + description: eMerchantPay username for the Genesis platform. + token: + type: string + description: eMerchantPay token for the Genesis platform. + password: + type: string + description: eMerchantPay password for the Genesis platform. + format: password + writeOnly: true + settings: + type: object + description: eMerchantPay settings object. + properties: + useGenesisPlatform: + type: boolean + description: >- + Specifies whether to use the Genesis platform on + eMerchantPay. + default: false + threeDSecureServer: + $ref: '#/components/schemas/ThreeDSecureIO3dsServer' + ecoPayz: + description: ecoPayz gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: ecoPayz credentials object. + properties: + paymentPageId: + type: string + description: ID of the ecoPayz payment page. + merchantAccountNumber: + type: string + description: ecoPayz merchant account number. + merchantPassword: + type: string + description: ecoPayz merchant password. + format: password + writeOnly: true + required: + - paymentPageId + - merchantAccountNumber + - merchantPassword + settings: + type: object + description: ecoPayz settings object. + properties: + validCurrency: + type: string + description: Three letter currency code in ISO 4217 format. + enum: + - CAD + - EUR + - GBP + - USD + required: + - validCurrency + iCanPay: + description: iCanPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + authenticateId: + type: string + description: Authenticate ID of the iCanPay account. + authenticatePw: + type: string + description: iCanPay authenticate password. + format: password + writeOnly: true + publicKey: + type: string + description: iCanPay API public key. + secretKey: + type: string + description: iCanPay API secret key. + format: password + writeOnly: true + required: + - authenticateId + - authenticatePw + - publicKey + - secretKey + settings: + type: object + properties: + use3DSEndpoint: + type: boolean + description: Specifies whether to use a 3DS endpoint. + iCheque: + description: iCheque gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + clientId: + type: string + description: ID of the iCheque client. + secretWord: + type: string + description: iCheque secret word. + format: password + writeOnly: true + apiUserId: + type: string + description: >- + Username for the Alliance API that is used for transaction + reporting. + apiSecurityToken: + type: string + description: >- + Hash of the password for the Alliance API that is used for + transaction reporting. + format: password + writeOnly: true + required: + - clientId + - secretWord + iDebit: + description: iDebit gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: iDebit credentials object. + properties: + merchantId: + type: string + description: iDebit merchant account number. + password: + type: string + description: iDebit merchant account password. + format: password + writeOnly: true + required: + - merchantId + - password + settings: + type: object + properties: + merchantSubId: + type: integer + description: ID of the merchant or organization. + vegaaH: + description: vegaaH gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + properties: + terminalId: + type: string + description: ID of the vegaaH terminal. + password: + type: string + description: vegaaH password. + format: password + writeOnly: true + required: + - terminalId + - password + XPay: + description: XPay gateway configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: XPay credentials object. + properties: + merchantId: + type: string + description: ID of the XPay merchant. + encryptionKey: + type: string + format: password + writeOnly: true + description: XPay encryption key. + required: + - merchantId + - encryptionKey + Zimpler: + description: Zimpler configuration. + allOf: + - $ref: '#/components/schemas/GatewayAccount' + - type: object + required: + - credentials + properties: + credentials: + type: object + description: Zimpler credentials object. + properties: + merchantId: + type: string + description: ID of the Zimpler merchant. + apiKey: + type: string + description: Zimpler API key. + format: password + writeOnly: true + required: + - merchantId + - apiKey + DigitalWallets: + type: object + description: Configure which digital wallets are enabled. + title: Digital Wallets + properties: + applePay: + type: object + description: |- + Apple Pay digital wallet configuration. If not using Apple Pay, + do not use this field. + required: + - isEnabled + properties: + isEnabled: + type: boolean + description: Specifies if Apple Pay is enabled. + default: false + displayName: + description: >- + String of 64, or fewer, UTF-8 characters containing the + canonical name for your store, + + which is suitable for display. + + Do not localize this name. + type: + - string + - 'null' + example: Test merchant + country: + $ref: '#/components/schemas/Country' + googlePay: + type: object + description: |- + Google Pay™ digital wallet configuration. If not using Google Pay™, + do not use this field. + required: + - isEnabled + properties: + isEnabled: + type: boolean + description: Specifies if Google Pay™ is enabled. + default: false + merchantName: + description: Merchant name in Google Pay™. + type: + - string + - 'null' + example: Test merchant + merchantOrigin: + description: >- + Merchant origin in Google Pay™. This uses the fully qualified + domain name. + type: + - string + - 'null' + example: www.example.com + country: + $ref: '#/components/schemas/Country' + ValidationErrorExtensions: + type: object + properties: + invalidFields: + description: Invalid field details. + type: array + items: + type: object + properties: + field: + type: string + description: |- + Name of the field. + Dot notation is used for nested object field names. + message: + description: Message field. + type: string + example: + - field: field1 + message: field1 is invalid + - field: subObject.field2 + message: field2 is invalid + - field: subObject.field2 + message: another error in the field2 + InvalidError: + description: Invalid data sent. + allOf: + - $ref: '#/components/schemas/Error' + - $ref: '#/components/schemas/ValidationErrorExtensions' + GatewayAccountDowntimeSchedule: + type: object + required: + - startTime + - endTime + properties: + id: + type: string + description: ID of the gateway account downtime schedule. + readOnly: true + maxLength: 50 + example: gw_dwn_0YVCXRK9TKCC6AMAPFB9M4RXDP + status: + description: Status of the gateway account downtime schedule. + readOnly: true + type: string + enum: + - pending + - ongoing + - finished + reason: + description: Reason for the gateway account downtime schedule. + readOnly: true + type: string + enum: + - scheduled-maintenance + - daily-limit-reached + - monthly-limit-reached + startTime: + description: Date and time when the gateway account scheduled downtime starts. + type: string + format: date-time + endTime: + description: Date and time when the gateway account scheduled downtime ends. + type: string + format: date-time + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + GatewayAccountLimit: + description: Gateway account limit details. + type: object + required: + - cap + properties: + id: + description: ID of the gateway account limit. + readOnly: true + $ref: '#/components/schemas/ResourceId' + status: + description: Status of the gateway account limit. + readOnly: true + type: string + enum: + - monitoring + - reached + startTime: + description: Date and time when the gateway account limit starts. + type: string + format: date-time + readOnly: true + endTime: + description: |- + Date and time when the gateway account limit ends. + When a gateway account limit ends the limit resets. + type: string + format: date-time + readOnly: true + frequency: + description: Frequency at which the gateway account limit period resets. + type: string + readOnly: true + enum: + - daily + - monthly + type: + description: Type of gateway account limit. + type: string + readOnly: true + enum: + - money + - count + x-enumDescriptions: + money: >- + Limits the amount of money that a transaction can process on a + gateway account. If `money` is chosen, the currency is the report + currency. + count: >- + Limits the total number of transaction can be processed on a + gateway account. + cap: + description: >- + Limits the maximum allowed value. + + If `type` is set to `money`, the currency is the report currency. + + `cap` only applies to approved transactions of type `authorize` or + `sale`. + type: integer + example: 1000 + usage: + description: Usage of the gateway account limit during this period. + type: integer + example: 375 + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + GatewayAccountTimeline: + type: object + properties: + id: + type: string + description: ID of the timeline message. + readOnly: true + maxLength: 50 + example: tmln_0YV8Q9WEF5DTA8HFXS27D1G6GC + type: + description: Type of timeline message. + type: string + readOnly: true + enum: + - gateway-account-created + - gateway-account-changed + - gateway-account-enabled + - gateway-account-disabled + - gateway-account-down + - gateway-account-up + - gateway-account-closed + - gateway-account-limit-reached + - gateway-account-limit-reset + - gateway-account-onboarding-completed + - gateway-account-onboarding-failed + triggeredBy: + description: 'Specifies who, or what, triggered the timeline event.' + type: string + readOnly: true + enum: + - rebilly + - app + - direct-api + message: + description: Contents of the timeline message. + type: string + extraData: + $ref: '#/components/schemas/TimelineExtraData' + occurredTime: + description: Date and time when the timeline message occurred. + readOnly: true + $ref: '#/components/schemas/ServerTimestamp' + _links: + $ref: '#/components/schemas/SelfLink' + GatewayAccountFinancialSettings: + type: object + description: Financial settings. + required: + - settlementSettings + properties: + settlementSettings: + $ref: '#/components/schemas/SettlementSettings' + riskReserveSettings: + description: |- + Risk reserve settings. + Use these settings to set up funds reserves before settlement. + type: array + items: + type: object + required: + - filter + - bips + - period + properties: + filter: + description: >- + Filter that is based on the properties of the transaction and + used to determine when to apply the settings. + minLength: 1 + maxLength: 255 + example: 'paymentInstrument.method:payment-card' + type: string + bips: + $ref: '#/components/schemas/Bips' + period: + description: Instruction for calculating the reserve release time. + $ref: '#/components/schemas/SchedulingMethodDateInterval' + DigitalWalletOnboardingApplePay: + type: object + description: Register a domain with Apple Pay and associate it with Rebilly. + required: + - domain + properties: + domain: + description: Domain to register. + type: string + status: + description: Status of Apple Pay domain registration. + type: string + readOnly: true + enum: + - failed + - registered + - already-registered + GridSegment: + type: object + required: + - data + - owner + - scope + properties: + id: + type: string + description: ID of the segment. + readOnly: true + maxLength: 50 + example: grd_seg_0YVCNRJW0ADV49TN8BD9X91XB5 + owner: + description: Creator of segment. + readOnly: true + type: object + properties: + id: + description: User ID of the creator. + type: string + $ref: '#/components/schemas/ResourceId' + name: + description: First and last name of the creator. + type: string + data: + description: >- + Object that contains a schema that is used to set up the UI for the + segment. + + This schema is built and consumed by the frontend. + + It includes information such as: segment name, UI settings, and so + on. + type: object + isStarred: + description: |- + Specifies if this segment is starred by the current user. + Starring a segment marks it as a favorite segment, + and also adds the segment to a starred folder in the UI. + type: boolean + isVisible: + description: Specifies if the segment is visible to the current user. + type: boolean + userIds: + type: array + uniqueItems: true + writeOnly: true + description: |- + List of user IDs with which the segment is shared. + This field is used when the `scope` field is set to `shared`. + items: + description: ID of the user. + $ref: '#/components/schemas/ResourceId' + users: + type: array + uniqueItems: true + description: List of user details with which this segment is shared. + readOnly: true + items: + type: object + description: User details. + properties: + id: + description: ID of the user. + type: string + $ref: '#/components/schemas/ResourceId' + name: + description: First and last name. + type: string + scope: + description: Controls the visibility of the segment. + type: string + enum: + - private + - public + - shared + x-enumDescriptions: + private: Visible only to the owner the segment. + public: Visible to all in the owner's organization. + shared: >- + Visible to a specific group of users that are specified in the + `userIds` field. + systemId: + description: >- + If the segment is derived from a system segment, + + this field contains the ID of the related system segment. + + To provide this field in a request, the `scope` field must be set to + `private` . + writeOnly: true + type: string + Integration: + type: object + properties: + service: + readOnly: true + description: Name of the integration service. + type: string + enum: + - google-sheets + - keap-infusionsoft + - intuit-quickbooks + count: + readOnly: true + type: integer + minimum: 0 + description: Total number of available rule sets. + configurations: + readOnly: true + type: array + minItems: 0 + description: List of configurations. + items: + type: object + properties: + labels: + description: Labels of the configuration. + type: array + minItems: 0 + items: + type: string + eventType: + $ref: '#/components/schemas/EventType' + title: + type: string + description: Title of the configuration. + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - oauth2Connect + ValueList: + type: object + required: + - description + - values + properties: + id: + type: string + description: ID of the list. + readOnly: true + maxLength: 50 + example: lst_0YVCXVQH84DEF93D8VBTJQ9TGK + version: + description: Version of the list. + type: integer + readOnly: true + minimum: 1 + description: + description: Description of the list. + type: string + values: + description: Values in the list. + type: array + items: + type: string + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + Membership: + type: object + required: + - organization + - user + properties: + organization: + description: Organization membership. + readOnly: true + type: object + properties: + id: + description: ID of the organization. + type: string + $ref: '#/components/schemas/ResourceId' + name: + description: Name of the organization. + type: string + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + user: + description: Membership user. + readOnly: true + type: object + properties: + id: + type: string + description: ID of the user. + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + name: + description: First and last name of the user. + type: string + email: + description: Email address of the user. + type: string + format: email + maxLength: 100 + allowedIps: + $ref: '#/components/schemas/AllowedIps' + permissions: + description: |- + Permissions that the user has within the organization. + Use the wildcard character `*` for full access. + $ref: '#/components/schemas/AclPermissions' + isOwner: + description: Specifies if the user is the owner of the organization. + type: boolean + isDefault: + description: >- + Specifies if the organization is the default organization for the + user. + type: boolean + readOnly: true + roleIds: + type: array + description: >- + Role IDs associated with the user. + + Role IDs specify the roles that the user performs within the + organization. + + For example, the user may be an organization admin. + items: + type: string + acl: + readOnly: true + allOf: + - $ref: '#/components/schemas/Acl' + _links: + $ref: '#/components/schemas/SelfLink' + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + roles: + type: array + Organization: + type: object + required: + - name + - country + properties: + id: + type: string + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + readOnly: true + maxLength: 50 + example: org_0YVDM8RC7GDADADSBSMW124JA8 + name: + description: Name of the organization. + type: string + maxLength: 60 + x-sortable: true + website: + description: |- + Website URL of the organization. + This value must begin with `https://` or `http://`. + type: string + maxLength: 65 + writeOnly: true + format: url + example: 'https://example.com' + address: + description: Street address of the organization. + type: + - string + - 'null' + maxLength: 60 + x-sortable: true + address2: + description: Second line of the street address. + type: + - string + - 'null' + maxLength: 60 + x-sortable: true + city: + description: City where the organization is located. + type: + - string + - 'null' + maxLength: 45 + x-sortable: true + region: + description: Region or state where the organization is located. + type: + - string + - 'null' + maxLength: 45 + x-sortable: true + country: + description: >- + Country where the organization is located, in [ISO Alpha-2 code + format](https://www.iso.org/obp/ui/#search/code/). + type: + - string + - 'null' + pattern: '^[A-Z]{2}$' + x-sortable: true + postalCode: + description: Postal code of the organization. + type: string + maxLength: 10 + x-sortable: true + phoneNumbers: + $ref: '#/components/schemas/ContactPhoneNumbers' + emails: + $ref: '#/components/schemas/ContactEmails' + taxDescriptor: + description: >- + Tax label of the organization. This information is displayed on the + invoice. + type: + - string + - 'null' + maxLength: 255 + invoiceTimeZone: + description: >- + Specifies the time zone to display on an invoice. UTC is used by + default. + type: string + maxLength: 50 + example: America/New_York + reportCurrency: + description: |- + Currency to use for conversion in reports. + This value cannot be changed. + type: string + pattern: '^[A-Z]{3}$' + example: USD + questionnaire: + type: + - object + - 'null' + description: Organization questionnaire. + properties: + role: + type: + - string + - 'null' + description: Role of the owner. + monthlyTransactions: + type: + - string + - 'null' + description: Amount of monthly processed transactions. + products: + type: + - array + - 'null' + description: List of products the organization is interested in. + items: + type: string + integrationType: + type: + - string + - 'null' + description: Type of integration the organization would like. + launchTiming: + type: + - string + - 'null' + description: When the organization would like to go live. + settings: + type: object + description: Organization settings. + properties: + defaultTaxCalculator: + type: object + description: Tax calculation settings. + required: + - type + properties: + type: + type: string + description: Type of tax calculator. + enum: + - taxjar + - avalara + - flat + rate: + type: + - number + - 'null' + format: float + description: Rate for flat tax calculation. + billing: + description: Global organization settings for billing. + type: + - object + - 'null' + properties: + pendingOrderTtl: + description: >- + Length of time, in ISO-8601 durations format, before which a + pending order is automatically abandoned. + type: + - string + - 'null' + example: P7D + default: null + taxLocations: + type: array + description: >- + Additional organization addresses where a merchant may want to + collect taxes using the `taxjar` tax calculator. + + + Available only for US-based organizations. + items: + type: object + required: + - country + properties: + address: + description: Street address of the tax location. + type: + - string + - 'null' + maxLength: 60 + city: + description: City of the tax location. + type: + - string + - 'null' + maxLength: 45 + region: + description: Region or state of the tax location. + type: + - string + - 'null' + maxLength: 45 + country: + description: >- + Country of the tax location, in [ISO Alpha-2 code + format](https://www.iso.org/obp/ui/#search/code/). + type: string + pattern: '^[A-Z]{2}$' + postalCode: + description: Postal code of the tax location. + type: + - string + - 'null' + maxLength: 10 + notifications: + description: Organization access-related notification settings. + type: object + properties: + notifyOnUserAccessChanges: + description: >- + Specifies whether to send notifications when users are + created or updated. + type: boolean + default: false + notifyOnApiKeyAccessChanges: + description: >- + Specifies whether to send notifications when API keys are + created or updated. + type: boolean + default: false + notificationEmailAddresses: + description: List of email addresses to send notifications to. + type: array + items: + type: string + format: email + default: [] + taxNumbers: + type: + - array + - 'null' + description: Tax numbers of the organization. + items: + $ref: '#/components/schemas/TaxNumber' + features: + type: array + description: Organization features. + readOnly: true + items: + type: object + properties: + name: + description: Name of the feature. + type: string + description: + description: Description of the feature. + type: string + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - attachments + CountriesUnrestrictedMetadata: + type: object + title: Unrestricted + required: + - mode + properties: + mode: + type: string + enum: + - unknown + - all + values: + description: List of supported countries. + type: array + uniqueItems: true + items: + type: string + minLength: 2 + maxLength: 2 + pattern: '^[A-Z]{2}$' + CountriesSubsetMetadata: + type: object + title: Subset + required: + - mode + - values + properties: + mode: + type: string + enum: + - subset + values: + description: List of supported countries. + type: array + minItems: 1 + uniqueItems: true + items: + type: string + minLength: 2 + maxLength: 2 + pattern: '^[A-Z]{2}$' + CountriesMetadata: + type: object + description: Countries metadata. + discriminator: + propertyName: mode + mapping: + unknown: '#/components/schemas/CountriesUnrestrictedMetadata' + all: '#/components/schemas/CountriesUnrestrictedMetadata' + subset: '#/components/schemas/CountriesSubsetMetadata' + anyOf: + - $ref: '#/components/schemas/CountriesUnrestrictedMetadata' + - $ref: '#/components/schemas/CountriesSubsetMetadata' + PaymentMethodMetadata: + type: object + additionalProperties: false + required: + - apiName + - name + - summary + - description + - countries + properties: + apiName: + description: Name of the payment method returned in the API response. + type: string + pattern: '^[\w\. -]+$' + name: + description: Name of the payment method. + type: string + pattern: '^[\w\. -]+$' + landscapeLogo: + description: URL for the payment method logo optimized for landscape orientation. + type: + - string + - 'null' + format: uri + portraitLogo: + description: URL for the payment method logo optimized for portrait orientation. + type: + - string + - 'null' + format: uri + summary: + description: |- + Short description of the payment method. + This field supports Markdown. + type: string + description: + description: |- + Detailed information about the payment method. + This field supports Markdown. + type: string + countries: + $ref: '#/components/schemas/CountriesMetadata' + storefrontEnabled: + type: boolean + default: false + _links: + $ref: '#/components/schemas/SelfLink' + PaymentGatewayMetadata: + type: object + additionalProperties: false + required: + - apiName + - logo + - paymentMethods + - merchantCountries + - currencies + properties: + apiName: + description: Name of the payment gateway returned in the API response. + type: string + pattern: '^[\w\. -]+$' + otherNames: + description: |- + List of former names associated with the payment gateway. + Gateways may change their names or may also be known by other names. + Tracking this list may help customer support and developers. + type: array + items: + type: object + additionalProperties: false + required: + - name + properties: + name: + description: Alternative or former name of the payment gateway. + type: string + description: + description: Short description about this name. + type: string + logo: + description: URL for the logo used with this name. + type: string + format: uri + logo: + description: URL for the logo of the gateway. + type: string + format: uri + summary: + description: |- + Short description of the payment gateway. + This field supports Markdown. + type: + - string + - 'null' + homepage: + description: URL of the payment gateway home page. + type: + - string + - 'null' + format: uri + externalDocs: + description: >- + List of links to the documentation of the payment gateway. + + For example, online API guides provided by gateways or a link to the + documents in Google Drive. + type: array + items: + type: object + additionalProperties: false + required: + - url + properties: + url: + description: |- + URL of the target documentation. + This value must be in URL format. + type: string + format: uri + description: + description: Short description of the target documentation. + type: string + publishedPricing: + description: >- + Pricing description for the payment gateway, if pricing is + published. + type: + - string + - 'null' + setupInstructions: + description: >- + Special gateway account set up instructions for merchants. + + For example: After adding this gateway account, + + set the IPN to `//example.com/ipns/{gateway-name}/{organization-id}` + by contacting your account rep. + type: + - string + - 'null' + paymentMethods: + description: >- + Array of supported payment methods. For example, `payment-card` and + `bitcoin`. + type: array + items: + description: Name of the payment method returned in the API response. + type: string + pattern: '^[\w\. -]+$' + minLength: 1 + cardBrands: + description: |- + Array of supported card-brands. + If the payment gateway supports payment cards. + type: array + items: + $ref: '#/components/schemas/PaymentCardBrand' + merchantCountries: + $ref: '#/components/schemas/CountriesMetadata' + currencies: + type: object + description: Currencies metadata. + required: + - mode + oneOf: + - title: Unrestricted + properties: + mode: + description: Specifies how the currencies are compared. + type: string + enum: + - unknown + - all + - title: Subset + required: + - values + properties: + mode: + description: Specifies how the currencies are compared. + type: string + enum: + - subset + values: + description: List of supported currencies. + type: array + minItems: 1 + uniqueItems: true + items: + $ref: '#/components/schemas/CurrencyCode' + operations: + description: Array of operations that are supported by the payment gateway. + type: array + items: + type: string + enum: + - verify + - auth + - capture + - sale + - refund + - query + - credit + - 3DS1 + - 3DS2 + - checkCredentials + supported3dsServers: + description: Array of supported 3DS-servers. + type: array + items: + type: string + supportedPaymentInstrumentSetupInstructions: + description: >- + Array of set up instructions that are supported by the payment + gateway. + type: array + items: + type: string + enum: + - do-nothing + - authorize + - authorize-and-void + - sca + x-enumDescriptions: + authorize: >- + Creates an `authorize` transaction in the amount or currency of + the request. + + This is used when a gateway account is configured for Strong + Customer Authentication (SCA). + authorize-and-void: >- + Creates an `authorize` transaction in the amount and currency of + the request, + + followed by a `void`, if the `authorize` is approved. + + This is used when a gateway account is configured for Strong + Customer Authentication (SCA). + sca: >- + Uses Strong Customer Authentication (SCA) without an `authorize` + transaction. + + SCA includes 3DS, and specific wallet behavior, + + such as setting up a billing agreement with PayPal. + do-nothing: |- + Does nothing except return an approved `setup` transaction. + This is the default behavior. + reconciliationSupport: + description: Specifies whether transactions can be reconciled. + type: boolean + default: false + disputeSupport: + description: >- + Specifies whether dispute data entry can be automated with an API or + SFTP access. + + For example, chargeback, retrieval, or TC-40 disputes. + type: boolean + default: false + offsite: + description: |- + Specifies whether offsite interaction is required. + For example, 3DS, PayPal login, or completing something in a store. + type: boolean + default: false + ipn: + type: + - object + - 'null' + description: >- + Describes the Instant Payment Notification (IPN) that a payment + gateway supports. + + If this value is null, IPN is not supported. + required: + - type + properties: + type: + description: |- + Type of supported Instant Payment Notification (IPN). + If this value is null, IPN is not supported. + type: string + enum: + - static + - dynamic + verificationMethod: + description: >- + Method used to verify that the data in the IPN call can be + trusted. + type: string + enum: + - query + - signature + default: query + recommendedWaitingApprovalTtl: + description: Recommended Time To Live (TTL) before abandoning the transaction. + type: integer + default: 3600 + minimum: 299 + maximum: 16777216 + _links: + $ref: '#/components/schemas/SelfLink' + PayoutRequest: + type: object + required: + - websiteId + - customerId + - currency + - amount + properties: + id: + type: string + readOnly: true + description: Unique resource ID. + maxLength: 50 + example: pout_req_0YVDMDE2BMC6KBB5MX76RF6T80 + websiteId: + $ref: '#/components/schemas/WebsiteId' + customerId: + description: ID of the customer who is requesting a payout. + $ref: '#/components/schemas/CustomerId' + paymentInstrumentId: + type: + - string + - 'null' + description: ID of the requested payment instrument to offer for the payout. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + currency: + description: Currency of the payout. + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Amount of the payout. + type: number + format: double + x-type: Money + availableAmount: + description: Available payout request amount that has not been allocated. + readOnly: true + type: number + format: double + x-type: Money + description: + description: Description of payout request. + type: + - string + - 'null' + status: + description: Status of the request. + type: string + readOnly: true + enum: + - pending + - instrument-selected + - partially-fulfilled + - fulfilled + x-enumDescriptions: + pending: >- + Request is awaiting customer's selection of the payment instrument + or fulfillment. + instrument-selected: >- + Request has a selected payment instrument and is awaiting + fulfillment. + partially-fulfilled: >- + Request is partially paid out when `availableAmount` is less than + `amount`. + fulfilled: Request is fully paid out when `availableAmount` reaches zero. + selectPaymentInstrumentUrl: + readOnly: true + type: string + format: uri + description: URL for the customer to select a preferred payment instrument. + allocations: + type: array + description: >- + List of credit transactions that are allocated to reduce the + available amount of a payout request. + readOnly: true + items: + type: object + properties: + transactionId: + description: >- + ID of the transaction to which the payout request is + allocated. + $ref: '#/components/schemas/TransactionId' + transactionResult: + description: Result of a transaction. + type: string + x-basic: true + readOnly: true + enum: + - abandoned + - approved + - canceled + - declined + - unknown + gatewayName: + description: >- + Name of the payment gateway that processed, or is selected to + process, the transaction. + x-label: Gateway account + x-basic: true + $ref: '#/components/schemas/GatewayName' + paymentInstrumentId: + type: string + description: ID of the selected payment instrument for the transaction. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + amount: + description: >- + Payout amount that is allocated from the payout request to the + credit transaction. + type: number + format: double + createdTime: + description: Date and time when a payout request is allocated. + $ref: '#/components/schemas/ServerTimestamp' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + selectedPaymentInstrumentRedirectUrl: + description: >- + URL where the customer is redirected when a payment instrument is + selected. The default value is the website URL. + + Use `{{id}}` as a placeholder for the payout request ID. + type: string + format: uri + example: 'https://example.com/payout-request-success?id={{id}}' + createdTime: + description: Date and time when the payout request is created. + $ref: '#/components/schemas/ServerTimestamp' + updatedTime: + description: Date and time when the payout request is updated. + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - paymentInstrument + Session: + type: object + required: + - permissions + properties: + id: + type: string + description: ID of the session. + readOnly: true + maxLength: 50 + example: jwt_0YV7DEJX80CDRAKVTV478ZNJDR + token: + description: Authentication token of the session. + type: string + permissions: + description: |- + Permissions of the session. + See the format in example. + Use wildcard `*` for full access. + $ref: '#/components/schemas/AclPermissions' + userId: + type: string + description: ID of the user. + readOnly: true + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + expiredTime: + description: Date and time when the session expired. Defaults to one hour. + type: string + format: date-time + _links: + $ref: '#/components/schemas/SelfLink' + OrderPreview: + type: object + required: + - websiteId + - items + properties: + websiteId: + writeOnly: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + items: + description: Items details. + type: array + writeOnly: true + minItems: 1 + items: + type: object + required: + - planId + properties: + planId: + type: string + description: ID of the plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + quantity: + description: Number of product units in the specified plan. + type: integer + billingAddress: + description: Billing address details. + writeOnly: true + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + deliveryAddress: + description: Delivery address details. + writeOnly: true + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + couponIds: + type: array + writeOnly: true + description: List of coupons that are applied to the order. + items: + type: string + currency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + lineItems: + type: array + description: List of purchase items. + readOnly: true + items: + type: object + properties: + type: + description: Type of transaction. + type: string + enum: + - debit + - credit + description: + description: Description of the purchase item. + type: string + unitPrice: + description: Unit price of the purchase item. + type: number + format: double + example: 49.95 + quantity: + description: Total quantity of the purchase item. + type: number + format: integer + price: + description: Total cost of the purchase item. + type: number + format: double + planId: + type: string + description: ID of the related plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + productId: + type: string + description: ID of the related product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + shippingRates: + type: array + description: Available shipping rates. + readOnly: true + items: + $ref: '#/components/schemas/ShippingOption' + taxes: + type: array + description: Taxes applied to the purchase. + readOnly: true + items: + type: object + properties: + amount: + description: Amount of tax that is due. + type: number + format: double + description: + type: string + description: Description of the tax. + discounts: + type: array + description: Discounts applied to the purchase. + readOnly: true + items: + type: object + properties: + couponId: + type: string + description: ID of a coupon that is associated with the purchase. + maxLength: 50 + example: cpn_0YVCNKF81GD778N4YNVGDJK558 + amount: + description: Total amount of discount from the coupon. + type: number + format: double + description: + description: Description of the discount. + type: string + subtotalAmount: + description: Subtotal amount of the purchase. + type: number + format: double + readOnly: true + taxAmount: + description: Tax amount of the purchase. + type: number + format: double + readOnly: true + shippingAmount: + description: Shipping amount of the purchase. + type: number + format: double + readOnly: true + discountsAmount: + description: Total discount amount of the purchase. + type: number + format: double + readOnly: true + total: + description: Total amount of the purchase. + type: number + format: double + readOnly: true + shipping: + writeOnly: true + $ref: '#/components/schemas/Shipping' + GlobalEventType: + type: string + description: Type of event. + enum: + - aml-list-possibly-matched + - application-instance-disabled + - application-instance-enabled + - balance-transaction-settled + - coupon-application-removed + - coupon-applied + - coupon-expiration-modified + - coupon-expired + - coupon-issued + - coupon-modified + - coupon-redeemed + - coupon-redemption-canceled + - credit-memo-applied + - credit-memo-created + - credit-memo-modified + - credit-memo-partially-applied + - credit-memo-voided + - customer-created + - customer-merged + - customer-one-time-password-requested + - customer-redirected-offsite + - customer-returned + - customer-updated + - data-export-completed + - dispute-created + - dispute-modified + - experian-check-performed + - gateway-account-downtime-ended + - gateway-account-downtime-started + - gateway-account-limit-reached + - gateway-account-onboarding-completed + - gateway-account-onboarding-failed + - gateway-account-requested + - hard-usage-limit-reached.yaml + - invoice-abandoned + - invoice-created + - invoice-issued + - invoice-modified + - invoice-paid + - invoice-partially-paid + - invoice-partially-refunded + - invoice-past-due + - invoice-past-due-reminder + - invoice-refunded + - invoice-reissued + - invoice-tax-calculation-failed + - invoice-voided + - kyc-document-accepted + - kyc-document-archived + - kyc-document-created + - kyc-document-modified + - kyc-document-rejected + - kyc-document-reviewed + - kyc-request-attempted + - kyc-request-failed + - kyc-request-fulfilled + - kyc-request-partially-fulfilled + - lead-source-changed + - nsf-response-received + - offsite-payment-completed + - order-abandoned + - order-completed + - payment-card-created + - payment-card-expiration-reminder + - payment-card-expired + - payment-instrument-modified + - payout-request-created + - payout-request-modified + - renewal-invoice-issued + - renewal-invoice-payment-canceled + - renewal-invoice-payment-declined + - risk-score-changed + - soft-usage-limit-reached.yaml + - subscription-activated + - subscription-canceled + - subscription-churned + - subscription-downgraded + - subscription-modified + - subscription-pause-created + - subscription-pause-modified + - subscription-pause-revoked + - subscription-paused + - subscription-reactivated + - subscription-renewal-reminder + - subscription-renewed + - subscription-resumed + - subscription-trial-converted + - subscription-trial-end-changed + - subscription-trial-end-reminder + - subscription-trial-ended + - subscription-upgraded + - transaction-amount-discrepancy-found + - transaction-declined + - transaction-discrepancy-found + - transaction-process-requested + - transaction-processed + - transaction-reconciled + - transaction-timeout-resolved + - waiting-gateway-transaction-completed + GlobalWebhook: + type: object + required: + - method + - url + - credentialHash + properties: + id: + type: string + description: ID of the webhook. + readOnly: true + maxLength: 50 + example: hook_0YVBG8S0K0DG59J6S3RMN9K452 + eventsFilter: + description: Array of system event types. + type: array + default: [] + items: + $ref: '#/components/schemas/GlobalEventType' + status: + x-basic: true + type: string + default: active + enum: + - active + - inactive + method: + type: string + x-basic: true + enum: + - GET + - POST + - PUT + - PATCH + - DELETE + url: + description: URL that triggers when a specified event occurs. + type: string + format: uri + headers: + description: Webhook headers. + type: array + items: + type: object + description: Webhook headers. + properties: + name: + description: Name of the property. + type: string + status: + description: Status of the property. + type: string + default: active + enum: + - active + - inactive + value: + description: Value of the property. + type: string + example: value1 + required: + - name + - value + credentialHash: + type: string + description: >- + Hash from credentials to use for authentication by the specified + URL. + body: + description: >- + Webhook request body. + + If this value is `null`, + + the default payload for the specified event is used. + + Template placeholders are permitted. + + + Invalid placeholders render as an empty strings. + + For example, `Hello {{invalid.placeholder}}!` is rendered as `Hello + !`. + + + For more information, + + see + [Placeholders](https://www.rebilly.com/docs/automations/email-notifications/#placeholders). + type: + - string + - 'null' + filter: + description: >- + Filter that determines whether to send the webhook. + + This field requires a special format. + + Use `,` for multiple allowed values. + + Use `;` for multiple fields. + + + For more information, + + see [Using + filters](https://all-rebilly.redoc.ly/#section/Using-filter-with-collections). + type: + - string + - 'null' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + x-label: Last update time + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + Profile: + type: object + properties: + id: + description: ID of the user. + readOnly: true + $ref: '#/components/schemas/ResourceId' + email: + description: Email address of the user. + readOnly: true + type: string + format: email + maxLength: 100 + firstName: + description: User's first name. + readOnly: true + type: string + lastName: + description: User's last name. + readOnly: true + type: string + businessPhone: + description: User's business phone number. + readOnly: true + type: + - string + - 'null' + mobilePhone: + description: User's mobile phone number. + readOnly: true + type: + - string + - 'null' + permissions: + description: |- + Permissions that the user has within organizations. + Use the wildcard character `*` for full access. + readOnly: true + allOf: + - $ref: '#/components/schemas/AclPermissions' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + availableCurrencies: + type: array + description: Array of reporting currencies that are enabled for the merchant. + readOnly: true + items: + type: string + reportingCurrency: + description: |- + User's currency code in ISO 4217 format. + This value is used for reports. + type: string + readOnly: true + loginTime: + description: Date and time when the user last logged in. + type: + - string + - 'null' + format: date-time + readOnly: true + status: + description: Status of the user account. + type: string + enum: + - active + - inactive + - pending-confirmation + readOnly: true + country: + description: >- + User's country of residence in ISO 3166 alpha-2 country code. + + For examples, see + [ISO.org](https://www.iso.org/obp/ui/#search/code/). + readOnly: true + type: string + preferences: + description: |- + User preferences, such as: timezone, language, and more. + This is an object with custom properties. + type: + - object + - 'null' + hasPermissionsEmulation: + type: boolean + description: >- + Specifies if the current user session has permissions emulation + enabled. + readOnly: true + displayName: + type: string + description: User's full display name. + readOnly: true + hash: + type: string + description: Unique hash value that represents the user. + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + ProfileMfa: + type: object + properties: + status: + description: Status of Multi-Factor Authentication (MFA) enrollment. + type: string + enum: + - active + - inactive + type: + description: >- + Type of MFA enrollment. + + Type `duo` cannot be updated or deleted. + + If you need to reset your Duo MFA, [Contact + support](https://www.rebilly.com/support/). + type: + - string + - 'null' + enum: + - duo + - guardian + - null + lastAuthTime: + description: >- + Date and time when MFA verification is most recently passed. + + To disable MFA, no more than 10 minutes must elapse between this + value and the request to disable MFA. + type: + - string + - 'null' + format: date-time + readOnly: true + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - enrollment + Role: + type: object + required: + - name + - acl + properties: + id: + type: string + readOnly: true + description: ID of the role. + maxLength: 50 + example: role_0YVDN2J11HDMX9N8X7DCB4CMX2 + name: + type: string + description: Name of the user role. + description: + type: + - string + - 'null' + description: Description of the role. + acl: + $ref: '#/components/schemas/Acl' + allowedIps: + $ref: '#/components/schemas/AllowedIps' + seniorIds: + type: array + description: List of senior role IDs. + readOnly: true + items: + type: string + juniorIds: + type: array + description: List of junior role IDs. + items: + type: string + usersCount: + type: integer + readOnly: true + description: Number of users assigned to this role. + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - seniorRoles + - juniorRoles + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + juniorRoles: + type: object + SendThroughAttribution: + type: object + description: Email notification sent through attribution. + readOnly: true + properties: + eventType: + $ref: '#/components/schemas/EventType' + actionId: + type: string + format: uuid + description: Action ID of the email notification. + versionId: + type: string + format: uuid + description: Version ID of the email notification. + sent: + type: integer + minimum: 0 + description: Number of email notifications that have been sent. + goal: + type: integer + minimum: 0 + description: Target number of email notifications to send. + Status: + type: object + properties: + status: + description: |- + Status of the Rebilly API. + If the API is operational, this value is `ok`. + type: string + readOnly: true + enum: + - ok + time: + description: Current date and time. + $ref: '#/components/schemas/ServerTimestamp' + ApiTracking: + type: object + description: API request tracking. + readOnly: true + properties: + id: + $ref: '#/components/schemas/ResourceId' + status: + type: integer + x-sortable: true + description: Response code of the HTTP request. + url: + type: string + x-basic: true + x-sortable: true + description: URL of the HTTP request. + route: + type: string + x-sortable: true + description: API request route. + example: '/customers/{param}' + method: + type: string + description: HTTP method of the API request. + enum: + - HEAD + - GET + - POST + - PUT + - DELETE + - PATCH + x-basic: true + request: + type: string + x-sortable: true + description: Request body of the HTTP request. + response: + type: string + x-sortable: true + description: Response body of the HTTP request. + requestHeaders: + $ref: '#/components/schemas/HttpHeaders' + responseHeaders: + $ref: '#/components/schemas/HttpHeaders' + user: + type: object + description: User who made the request. + readOnly: true + properties: + userId: + x-sortable: true + type: string + description: ID of the user. + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + apiKeyId: + type: string + description: ID of the API tracking log. + maxLength: 50 + example: api_key_0YV7JQMY6ED2FBE58BMFHBWBZH + email: + description: Email address of the user. + type: string + format: email + x-sortable: true + firstName: + description: First name of the user. + type: string + x-sortable: true + lastName: + description: Last name of the user. + type: string + x-sortable: true + ipAddress: + type: string + x-sortable: true + deprecated: true + description: Client IP address. + format: ipv4 + userAgent: + description: Software that is acting on behalf of the user. + type: string + x-sortable: true + fingerprint: + description: >- + User's device fingerprint hash. + + A device fingerprint is a unique token that is used to identify + the user. + + The device fingerprint is generated based on device attributes, + + such as: hardware, software, IP address, language, browser, and + more. + type: string + x-sortable: true + isSupport: + description: >- + Specifies if the API request is completed by a Rebilly Support + Team member. + type: boolean + x-sortable: true + ipAddress: + type: string + x-basic: true + description: Client IP address. + relatedResourceIds: + type: array + description: IDs of related resources. + readOnly: true + items: + type: string + example: + - in_0YVF9605RKC62BP14NE2R7V2XT + - cus_0YV7DDSDD1C8DA64KHH2W33CPF + duration: + type: integer + x-sortable: true + x-basic: true + description: Request duration in milliseconds. + organizationId: + $ref: '#/components/schemas/OrganizationId' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + organization: + type: object + TaxTracking: + type: object + description: Tax service tracking logs. + readOnly: true + properties: + id: + $ref: '#/components/schemas/ResourceId' + status: + type: integer + x-sortable: true + description: Response code of the HTTP request. + duration: + type: integer + x-sortable: true + description: Duration of the HTTP request in milliseconds. + initiatedTime: + description: Date and time the HTTP request is initiated. + x-sortable: true + $ref: '#/components/schemas/ServerTimestamp' + url: + type: string + x-sortable: true + description: URL of the tax service endpoint. + method: + type: string + x-sortable: true + description: Method of the HTTP request. + example: POST + request: + type: string + description: Request body of the HTTP request. + response: + type: string + description: Response body of the HTTP request. + requestHeaders: + $ref: '#/components/schemas/HttpHeaders' + responseHeaders: + $ref: '#/components/schemas/HttpHeaders' + entityId: + type: string + x-sortable: true + description: ID of the resource associated with the tax service request. + organizationId: + x-sortable: true + allOf: + - $ref: '#/components/schemas/OrganizationId' + taxService: + type: string + x-sortable: true + description: Name of the tax service used for the request. + enum: + - taxjar + - avalara + taxServiceCredentialSource: + type: string + x-sortable: true + description: Source of the credentials that are used to send or retrieve data. + enum: + - default + - merchant + customerId: + type: string + description: ID of the customer associated with the tax service request. + _links: + $ref: '#/components/schemas/SelfLink' + WebhookTracking: + type: object + description: Webhook tracking logs. + readOnly: true + properties: + id: + type: string + description: ID of the webhook tracking log. + maxLength: 50 + example: hook_log_0YVBG8S0K0DG59J6S3RMN9K452 + status: + type: integer + x-sortable: true + description: Response code of the webhook request. + duration: + type: integer + x-sortable: true + description: Duration of the webhook request in milliseconds. + initiatedTime: + description: Date and time when the webhook is initiated. + x-sortable: true + $ref: '#/components/schemas/ServerTimestamp' + url: + type: string + x-sortable: true + description: URL where webhook is sent. + method: + type: string + x-sortable: true + description: HTTP method used to send webhook. + example: POST + request: + type: string + description: Webhook payload. + response: + type: string + description: Response body received. + requestHeaders: + $ref: '#/components/schemas/HttpHeaders' + responseHeaders: + $ref: '#/components/schemas/HttpHeaders' + entityId: + type: string + x-sortable: true + description: ID of the main entity related to the event type. + organizationId: + x-sortable: true + allOf: + - $ref: '#/components/schemas/OrganizationId' + eventType: + x-sortable: true + allOf: + - $ref: '#/components/schemas/EventType' + source: + type: string + x-sortable: true + description: Webhook source. + enum: + - webhooks + - rules + attempt: + type: integer + x-sortable: true + description: Webhook attempt number. + sentTime: + x-sortable: true + description: Date and time when the webhook is sent. + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + x-sortable: true + $ref: '#/components/schemas/CreatedTime' + updatedTime: + x-sortable: true + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + User: + type: object + required: + - email + - firstName + - lastName + properties: + id: + type: string + description: ID of the user. + readOnly: true + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + email: + description: Email address of the user. + type: string + format: email + maxLength: 100 + firstName: + description: User's first name. + type: string + lastName: + description: User's last name. + type: string + businessPhone: + description: User's business phone number. + type: + - string + - 'null' + mobilePhone: + description: User's mobile phone number. + type: + - string + - 'null' + permissions: + description: |- + Permissions that the user has within organizations. + Use the wildcard character `*` for full access. + $ref: '#/components/schemas/AclPermissions' + computedPermissions: + description: >- + All user permissions and roles. Use these permissions to emulate the + user. + readOnly: true + allOf: + - $ref: '#/components/schemas/AclPermissions' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + loginTime: + description: Date and time when the user last logged in. + type: + - string + - 'null' + format: date-time + readOnly: true + reportingCurrency: + description: |- + User's currency code in ISO 4217 format. + This value is used for reports. + readOnly: true + type: string + availableCurrencies: + type: array + description: Array of reporting currencies that are enabled for the merchant. + readOnly: true + items: + type: string + status: + description: Status of the user's account. + type: string + enum: + - active + - inactive + - pending-confirmation + readOnly: true + country: + description: >- + User's country of residence in ISO 3166 alpha-2 country code. + + For examples, see + [ISO.org](https://www.iso.org/obp/ui/#search/code/). + type: string + preferences: + description: |- + User preferences, such as: timezone, language, and more. + This is an object with custom properties. + type: + - object + - 'null' + roleIds: + type: array + description: >- + Role IDs associated with the user. + + Role IDs specify the roles that the user performs within the + organization. + + For example, the user may be an organization admin. + items: + type: string + allowedIps: + $ref: '#/components/schemas/AllowedIps' + _links: + $ref: '#/components/schemas/SelfLink' + Website: + type: object + required: + - name + - url + - servicePhone + - serviceEmail + properties: + id: + readOnly: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + name: + description: Name of the website. + type: string + url: + description: Domain address of the website. + type: string + servicePhone: + description: Customer service phone number of the website. + type: string + serviceEmail: + description: Customer service email address of the website. + type: string + format: email + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + settings: + type: object + description: Website settings. + properties: + depositForm: + type: object + description: Deposit form settings. + properties: + theme: + description: Theme settings that are used in the deposit form. + type: + - object + - 'null' + properties: + colorPrimary: + description: >- + Primary color for the deposit form in hexadecimal + format. + type: + - string + - 'null' + maxLength: 6 + example: 0044d4 + colorSecondary: + description: >- + Secondary color for the deposit form in hexadecimal + format. + type: + - string + - 'null' + maxLength: 6 + example: ffffff + buttonTemplate: + description: >- + Submit button template. `{{amount}}` can be used as a + placeholder for amount and currency. + type: + - string + - 'null' + maxLength: 100 + example: 'Pay {{amount}}' + paymentForm: + type: object + description: Payment form settings. + properties: + css: + description: >- + Hosted payment form [CSS + options](https://www.rebilly.com/docs/content/concepts-and-features/tutorial/customize-style-rebilly-instruments/#2-use-css-property-to-override-any-styles). + type: + - string + - 'null' + theme: + description: >- + Hosted payment form [theme + options](https://www.rebilly.com/docs/content/concepts-and-features/tutorial/customize-style-rebilly-instruments/#adjust-the-default-style). + type: + - object + - 'null' + additionalProperties: + type: string + example: + colorPrimary: '#504CCA' + features: + description: Hosted payment form features. + type: + - object + - 'null' + properties: + showCoupons: + type: + - array + - 'null' + items: + type: string + fullPageRedirect: + type: + - boolean + - 'null' + description: >- + Specifies whether the hosted payment form uses a full + page redirect, or the default iframe modal, for approval + URL redirects. + default: false + skipRedirectOnPaymentComplete: + type: + - boolean + - 'null' + description: >- + Specifies whether the hosted payment form skips the + redirect to the website URL when the payment is + completed. + default: false + logoId: + type: + - string + - 'null' + description: ID of the linked file object. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + organizationId: + deprecated: true + readOnly: true + allOf: + - $ref: '#/components/schemas/OrganizationId' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - logoUrl + CustomerInformation: + type: object + properties: + currency: + description: Currency code in ISO 4217 format. + minLength: 3 + maxLength: 3 + example: USD + readOnly: true + type: string + refundsAmount: + description: Total amount of all refunded transactions. + readOnly: true + type: number + format: double + revenueAmount: + description: Total amount of all approved sales and captures. + readOnly: true + type: number + format: double + disputesAmount: + description: Total amount of all disputed transactions. + readOnly: true + type: number + format: double + DataExportArguments: + type: object + description: >- + Export request arguments used to filter and sort the result set. + + For more information, see + [Arguments](https://www.rebilly.com/docs/dev-docs/transaction-reconciliation/#arguments). + properties: + filter: + description: >- + Filters the collection items. + + This field requires a special format. + + Use `,` for multiple allowed values. Use `;` for multiple fields. + + + For more information, see [Using filter with + collections](#section/Using-filter-with-collections). + type: string + sort: + type: string + description: |- + Sorts and orders the collection of items. + To sort in descending order, prefix with `-`. + q: + type: string + description: Use this field to perform a partial search of text fields. + DataExportDateRange: + type: object + description: >- + Date range of the data export. + + If this value is not set, all data is included. + + For more information, see [Date + ranges](https://www.rebilly.com/docs/dev-docs/transaction-reconciliation/#date-ranges). + properties: + start: + type: string + description: >- + Start date of the data export. + + This field accepts any argument in [datetime + format](http://php.net/manual/en/datetime.formats.php). + example: yesterday + end: + type: string + description: >- + End date of the data export. + + This field accepts any argument in [datetime + format](http://php.net/manual/en/datetime.formats.php). + example: today + field: + type: string + description: Field to which the date range is applied. + required: + - start + - end + CustomersDataExport: + type: object + description: Customer resource to export. + required: + - name + - format + - resource + properties: + id: + type: string + description: ID of the export. + readOnly: true + maxLength: 50 + example: exp_0YVCNQ2C1AD1RA3HXKP492GNZB + name: + description: Name of the export. + type: string + resource: + description: Type of resource to export. + type: string + enum: + - customers + format: + description: Output format of the export. + type: string + enum: + - csv + - json + - json-api + - xml + arguments: + $ref: '#/components/schemas/DataExportArguments' + emailNotification: + description: List of email addresses to notify when an export is completed. + type: array + items: + type: string + format: email + fields: + description: >- + List of fields to include in the export. + + For more information, see + [Arguments](https://www.rebilly.com/docs/dev-docs/transaction-reconciliation/#fields). + type: array + items: + type: string + recurring: + description: Recurring export schedule. + type: object + required: + - instruction + properties: + instruction: + type: string + description: >- + Schedule instruction in [Recurrence Rule RFC + 5545](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) + format. + start: + type: string + format: date-time + description: |- + Date and time when the first scheduled recurring export occurs. + The default value is now. + userId: + description: ID of the user who requested the data export. + readOnly: true + type: string + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + fileId: + type: + - string + - 'null' + description: ID of the data export file. + readOnly: true + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + recordCount: + description: 'Total number of records in the export, excluding the header row.' + readOnly: true + type: integer + scheduledTime: + description: Date and time when the data export is scheduled to generate a file. + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + status: + description: Status of export request. + readOnly: true + type: string + enum: + - pending + - queued + - processing + - completed + dateRange: + allOf: + - $ref: '#/components/schemas/DataExportDateRange' + - type: object + properties: + field: + type: string + default: createdTime + example: createdTime + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - user + - download + - signedLink + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + file: + type: object + user: + type: object + SubscriptionsDataExport: + type: object + description: Order resource to export. + required: + - name + - format + - resource + properties: + id: + type: string + description: ID of the export. + readOnly: true + maxLength: 50 + example: exp_0YVCNQ2C1AD1RA3HXKP492GNZB + name: + description: Name of the export. + type: string + resource: + description: Type of resource to export. + type: string + enum: + - subscriptions + format: + description: Output format of the export. + type: string + enum: + - csv + - json + - json-api + - xml + arguments: + $ref: '#/components/schemas/DataExportArguments' + emailNotification: + description: List of email addresses to notify when an export is completed. + type: array + items: + type: string + format: email + fields: + description: >- + List of fields to include in the export. + + For more information, see + [Arguments](https://www.rebilly.com/docs/dev-docs/transaction-reconciliation/#fields). + type: array + items: + type: string + recurring: + description: Recurring export schedule. + type: object + required: + - instruction + properties: + instruction: + type: string + description: >- + Schedule instruction in [Recurrence Rule RFC + 5545](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) + format. + start: + type: string + format: date-time + description: |- + Date and time when the first scheduled recurring export occurs. + The default value is now. + userId: + description: ID of the user who requested the data export. + readOnly: true + type: string + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + fileId: + type: + - string + - 'null' + description: ID of the data export file. + readOnly: true + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + recordCount: + description: 'Total number of records in the export, excluding the header row.' + readOnly: true + type: integer + scheduledTime: + description: Date and time when the data export is scheduled to generate a file. + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + status: + description: Status of export request. + readOnly: true + type: string + enum: + - pending + - queued + - processing + - completed + dateRange: + allOf: + - $ref: '#/components/schemas/DataExportDateRange' + - type: object + properties: + field: + type: string + default: createdTime + example: createdTime + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - user + - download + - signedLink + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + file: + type: object + user: + type: object + TransactionsDataExport: + type: object + description: Transaction resource to export. + required: + - name + - format + - resource + properties: + id: + type: string + description: ID of the export. + readOnly: true + maxLength: 50 + example: exp_0YVCNQ2C1AD1RA3HXKP492GNZB + name: + description: Name of the export. + type: string + resource: + description: Type of resource to export. + type: string + enum: + - transactions + format: + description: Output format of the export. + type: string + enum: + - csv + - json + - json-api + - xml + arguments: + $ref: '#/components/schemas/DataExportArguments' + emailNotification: + description: List of email addresses to notify when an export is completed. + type: array + items: + type: string + format: email + fields: + description: >- + List of fields to include in the export. + + For more information, see + [Arguments](https://www.rebilly.com/docs/dev-docs/transaction-reconciliation/#fields). + type: array + items: + type: string + recurring: + description: Recurring export schedule. + type: object + required: + - instruction + properties: + instruction: + type: string + description: >- + Schedule instruction in [Recurrence Rule RFC + 5545](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) + format. + start: + type: string + format: date-time + description: |- + Date and time when the first scheduled recurring export occurs. + The default value is now. + userId: + description: ID of the user who requested the data export. + readOnly: true + type: string + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + fileId: + type: + - string + - 'null' + description: ID of the data export file. + readOnly: true + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + recordCount: + description: 'Total number of records in the export, excluding the header row.' + readOnly: true + type: integer + scheduledTime: + description: Date and time when the data export is scheduled to generate a file. + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + status: + description: Status of export request. + readOnly: true + type: string + enum: + - pending + - queued + - processing + - completed + dateRange: + allOf: + - $ref: '#/components/schemas/DataExportDateRange' + - type: object + properties: + field: + x-sortable: true + type: string + default: processedTime + example: processedTime + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - user + - download + - signedLink + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + file: + type: object + user: + type: object + InvoicesDataExport: + type: object + description: Invoice resource to export. + required: + - name + - format + - resource + properties: + id: + type: string + description: ID of the export. + readOnly: true + maxLength: 50 + example: exp_0YVCNQ2C1AD1RA3HXKP492GNZB + name: + description: Name of the export. + type: string + resource: + description: Type of resource to export. + type: string + enum: + - invoices + format: + description: Output format of the export. + type: string + enum: + - csv + - json + - json-api + - xml + - pdf + arguments: + $ref: '#/components/schemas/DataExportArguments' + emailNotification: + description: List of email addresses to notify when an export is completed. + type: array + items: + type: string + format: email + fields: + description: >- + List of fields to include in the export. + + For more information, see + [Arguments](https://www.rebilly.com/docs/dev-docs/transaction-reconciliation/#fields). + type: array + items: + type: string + recurring: + description: Recurring export schedule. + type: object + required: + - instruction + properties: + instruction: + type: string + description: >- + Schedule instruction in [Recurrence Rule RFC + 5545](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) + format. + start: + type: string + format: date-time + description: |- + Date and time when the first scheduled recurring export occurs. + The default value is now. + userId: + description: ID of the user who requested the data export. + readOnly: true + type: string + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + fileId: + type: + - string + - 'null' + description: ID of the data export file. + readOnly: true + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + recordCount: + description: 'Total number of records in the export, excluding the header row.' + readOnly: true + type: integer + scheduledTime: + description: Date and time when the data export is scheduled to generate a file. + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + status: + description: Status of export request. + readOnly: true + type: string + enum: + - pending + - queued + - processing + - completed + dateRange: + allOf: + - $ref: '#/components/schemas/DataExportDateRange' + - type: object + properties: + field: + type: string + default: issuedTime + example: issuedTime + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - user + - download + - signedLink + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + file: + type: object + user: + type: object + InvoiceItemsDataExport: + type: object + description: Invoice item resource to export. + required: + - name + - format + - resource + properties: + id: + type: string + description: ID of the export. + readOnly: true + maxLength: 50 + example: exp_0YVCNQ2C1AD1RA3HXKP492GNZB + name: + description: Name of the export. + type: string + resource: + description: Type of resource to export. + type: string + enum: + - invoiceItems + format: + description: Output format of the export. + type: string + enum: + - csv + - json + - json-api + - xml + arguments: + $ref: '#/components/schemas/DataExportArguments' + emailNotification: + description: List of email addresses to notify when an export is completed. + type: array + items: + type: string + format: email + fields: + description: >- + List of fields to include in the export. + + For more information, see + [Arguments](https://www.rebilly.com/docs/dev-docs/transaction-reconciliation/#fields). + type: array + items: + type: string + recurring: + description: Recurring export schedule. + type: object + required: + - instruction + properties: + instruction: + type: string + description: >- + Schedule instruction in [Recurrence Rule RFC + 5545](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) + format. + start: + type: string + format: date-time + description: |- + Date and time when the first scheduled recurring export occurs. + The default value is now. + userId: + description: ID of the user who requested the data export. + readOnly: true + type: string + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + fileId: + type: + - string + - 'null' + description: ID of the data export file. + readOnly: true + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + recordCount: + description: 'Total number of records in the export, excluding the header row.' + readOnly: true + type: integer + scheduledTime: + description: Date and time when the data export is scheduled to generate a file. + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + status: + description: Status of export request. + readOnly: true + type: string + enum: + - pending + - queued + - processing + - completed + dateRange: + allOf: + - $ref: '#/components/schemas/DataExportDateRange' + - type: object + properties: + field: + type: string + default: issuedTime + example: issuedTime + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - user + - download + - signedLink + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + file: + type: object + user: + type: object + RevenueAuditDataExport: + type: object + description: Revenue audit resource to export. + required: + - name + - format + - resource + properties: + id: + type: string + description: ID of the export. + readOnly: true + maxLength: 50 + example: exp_0YVCNQ2C1AD1RA3HXKP492GNZB + name: + description: Name of the export. + type: string + resource: + description: Type of resource to export. + type: string + enum: + - revenueAudit + format: + description: Output format of the export. + type: string + enum: + - csv + - json + - json-api + - xml + arguments: + $ref: '#/components/schemas/DataExportArguments' + emailNotification: + description: List of email addresses to notify when an export is completed. + type: array + items: + type: string + format: email + fields: + description: >- + List of fields to include in the export. + + For more information, see + [Arguments](https://www.rebilly.com/docs/dev-docs/transaction-reconciliation/#fields). + type: array + items: + type: string + recurring: + description: Recurring export schedule. + type: object + required: + - instruction + properties: + instruction: + type: string + description: >- + Schedule instruction in [Recurrence Rule RFC + 5545](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) + format. + start: + type: string + format: date-time + description: |- + Date and time when the first scheduled recurring export occurs. + The default value is now. + userId: + description: ID of the user who requested the data export. + readOnly: true + type: string + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + fileId: + type: + - string + - 'null' + description: ID of the data export file. + readOnly: true + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + recordCount: + description: 'Total number of records in the export, excluding the header row.' + readOnly: true + type: integer + scheduledTime: + description: Date and time when the data export is scheduled to generate a file. + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + status: + description: Status of export request. + readOnly: true + type: string + enum: + - pending + - queued + - processing + - completed + dateRange: + allOf: + - $ref: '#/components/schemas/DataExportDateRange' + - type: object + properties: + field: + x-sortable: true + type: string + default: scheduledTime + example: scheduledTime + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - user + - download + - signedLink + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + file: + type: object + user: + type: object + AmlChecksDataExport: + type: object + description: AML checks resource to export. + required: + - name + - format + - resource + properties: + id: + type: string + description: ID of the export. + readOnly: true + maxLength: 50 + example: exp_0YVCNQ2C1AD1RA3HXKP492GNZB + name: + description: Name of the export. + type: string + resource: + description: Type of resource to export. + type: string + enum: + - amlChecks + format: + description: Output format of the export. + type: string + enum: + - csv + - json + - json-api + - xml + arguments: + $ref: '#/components/schemas/DataExportArguments' + emailNotification: + description: List of email addresses to notify when an export is completed. + type: array + items: + type: string + format: email + fields: + description: >- + List of fields to include in the export. + + For more information, see + [Arguments](https://www.rebilly.com/docs/dev-docs/transaction-reconciliation/#fields). + type: array + items: + type: string + recurring: + description: Recurring export schedule. + type: object + required: + - instruction + properties: + instruction: + type: string + description: >- + Schedule instruction in [Recurrence Rule RFC + 5545](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) + format. + start: + type: string + format: date-time + description: |- + Date and time when the first scheduled recurring export occurs. + The default value is now. + userId: + description: ID of the user who requested the data export. + readOnly: true + type: string + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + fileId: + type: + - string + - 'null' + description: ID of the data export file. + readOnly: true + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + recordCount: + description: 'Total number of records in the export, excluding the header row.' + readOnly: true + type: integer + scheduledTime: + description: Date and time when the data export is scheduled to generate a file. + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + status: + description: Status of export request. + readOnly: true + type: string + enum: + - pending + - queued + - processing + - completed + dateRange: + allOf: + - $ref: '#/components/schemas/DataExportDateRange' + - type: object + properties: + field: + type: string + default: createdTime + example: createdTime + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - user + - download + - signedLink + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + file: + type: object + user: + type: object + DataExport: + type: object + discriminator: + propertyName: resource + mapping: + customers: '#/components/schemas/CustomersDataExport' + subscriptions: '#/components/schemas/SubscriptionsDataExport' + transactions: '#/components/schemas/TransactionsDataExport' + invoices: '#/components/schemas/InvoicesDataExport' + invoiceItems: '#/components/schemas/InvoiceItemsDataExport' + revenueAudit: '#/components/schemas/RevenueAuditDataExport' + amlChecks: '#/components/schemas/AmlChecksDataExport' + oneOf: + - $ref: '#/components/schemas/CustomersDataExport' + - $ref: '#/components/schemas/SubscriptionsDataExport' + - $ref: '#/components/schemas/TransactionsDataExport' + - $ref: '#/components/schemas/InvoicesDataExport' + - $ref: '#/components/schemas/InvoiceItemsDataExport' + - $ref: '#/components/schemas/RevenueAuditDataExport' + - $ref: '#/components/schemas/AmlChecksDataExport' + OrganizationExport: + type: object + properties: + id: + readOnly: true + type: string + description: Unique resource ID. + maxLength: 50 + example: org_exp_0YVDMCH30ZC2XA5BYHH6B6SRTJ + userId: + description: ID of the user who requested the organization data export. + readOnly: true + type: string + maxLength: 50 + example: usr_0YVCEENYJ3D7Q9EN6BN16HA0G4 + fileId: + type: + - string + - 'null' + description: ID of the linked file object. + readOnly: true + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + status: + description: Status of organization data export request. + readOnly: true + type: string + enum: + - pending + - processing + - completed + - queued + - failed + - expired + resources: + description: Organization data export resources array. + readOnly: true + type: array + items: + type: object + properties: + type: + description: Exported resource. + readOnly: true + type: string + enum: + - customers + - users + - payment-instruments + - invoices + - orders + - transactions + - disputes + - gateway-accounts + - blocklists + - lists + - webhooks + - products + - websites + - plans + - credit-memos + - files + - email-notifications + x-enumDescriptions: + customers: All customers of the organization. + users: All users of the organization. + payment-instruments: All payment instruments of the organization. + invoices: All invoices of the organization. + orders: All orders of the organization. + transactions: All transactions of the organization. + disputes: All disputes of the organization. + gateway-accounts: All gateway accounts of the organization. + blocklists: All blocklists of the organization. + lists: All lists of the organization. + webhooks: All webhooks of the organization. + products: All products of the organization. + websites: All websites of the organization. + plans: All plans of the organization. + credit-memos: All credit-memos of the organization. + files: All files of the organization. + email-notifications: All email-notifications of the organization. + recordCount: + description: Number of exported resources. + readOnly: true + type: integer + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + retentionTime: + description: |- + Date and time when retention ends. + After this date, files are removed. + readOnly: true + type: + - string + - 'null' + format: date-time + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - user + - signedLink + HistogramData: + title: Histogram data + type: object + properties: + data: + description: Histogram data. + type: array + items: + type: object + readOnly: true + properties: + date: + type: string + description: Date and time of data entry. + value: + type: number + description: Data entry value. + ApiLogSummary: + type: object + properties: + data: + description: API log summary data. + type: array + items: + type: object + description: Number of requests for each method. + readOnly: true + properties: + route: + type: string + description: Route string pattern. + total: + type: integer + description: Total number of requests. + get: + type: integer + description: Total number of `GET` requests. + post: + type: integer + description: Total number of `POST` requests. + put: + type: integer + description: Total number of `PUT` requests. + patch: + type: integer + description: Total number of `PATCH` requests. + delete: + type: integer + description: Total number of `DELETE` requests. + head: + type: integer + description: Total number of `HEAD` requests. + options: + type: integer + description: Total number of `OPTIONS` requests. + CumulativeSubscriptions: + type: object + properties: + data: + description: Cumulative subscription data. + type: array + items: + type: object + description: Contains an aggregation. + readOnly: true + properties: + aggregationValue: + type: string + description: >- + Date in `YYYY-MM` format for monthly aggregation, or + `YYYY-MM-DD` for daily aggregation. + newCount: + type: integer + description: Number of new subscriptions within the aggregation. + canceledCount: + type: integer + description: Number of canceled subscriptions within the aggregation. + cumulativeCount: + type: integer + description: >- + Total number of cumulative subscriptions. + + + This field is calculated based on: the number of cumulative + subscriptions from the previous aggregation period + + `newCount` - `canceledCount`. + DashboardResponse: + type: array + items: + type: object + properties: + metric: + type: string + description: Type of metric. + enum: + - approvalRate + - salesCount + - salesValue + - refundsValue + - chargebacksCount + - chargebacksValue + - transactionsCount + - redeemedCouponsCount + - newLeadsCount + - newCustomersCount + - appliedCouponsCount + - trialConversionsCount + - trialConversionsRate + - renewalSuccessRate + - renewalsCount + - newTrialsCount + - reactivationsCount + - successfulRetriesCount + - invoicedRevenue + - churnCount + - churnRate + - cancellationsCount + - cancellationsRate + - activeSubscriptionsCount + - newSubscriptionsCount + - upgradesCount + - downgradesCount + - monthlyRecurringRevenue + - averageRevenuePerCustomer + - customerLifetimeValue + - addressProofAcceptanceRate + - identityProofAcceptanceRate + - fundsProofAcceptanceRate + - purchaseProofAcceptanceRate + - creditFileProofAcceptanceRate + - kycRejectionRate + - kycAccuracyRate + - addressProofAccuracyRate + - identityProofAccuracyRate + - creditFileProofAccuracyRate + - kycRequestCount + - kycRequestAbandonmentRate + - kycRequestAttemptedRate + - kycRequestFailureRate + - kycRequestFulfillmentRate + - kycRequestExpirationRate + humanName: + type: string + description: Display name of the metric. + increaseIsGood: + type: boolean + description: >- + Specifies whether a higher value is a positive result for the + merchant. + segments: + type: array + description: Segment data. + items: + type: object + properties: + name: + type: string + description: Name of the segment. + value: + type: + - number + - 'null' + format: double + description: Segment value for the specified date range. + previousValue: + type: + - number + - 'null' + format: double + description: |- + Segment value for the previous date range. + This value is relative to the specified date range. + humanValue: + type: + - string + - 'null' + description: |- + Human readable segment value. + This field is formatted with a currency sign. + changeRatio: + type: + - number + - 'null' + format: double + description: |- + Ratio of the current value for each previous value. + A null value represents infinity. + humanChangeRatio: + type: + - string + - 'null' + description: |- + Human readable change ratio. + This field is formatted percentage sign. + A null value represents infinity. + timeseries: + description: Time series. + type: array + items: + type: object + description: Chart data. + readOnly: true + properties: + date: + type: string + description: Entry date-time. + value: + type: + - number + - 'null' + description: Entry value. + DccMarkup: + type: object + properties: + data: + description: Aggregation data. + type: array + items: + type: object + description: Contains an aggregation. + readOnly: true + properties: + aggregationValue: + type: string + description: |- + Value by which the report provides aggregated data. + Date values are displayed as follows: + + - Day: `YYYY-MM-DD`. + - Month: `YYYY-MM`. + selectedCount: + type: integer + description: Number of selected offers in the aggregation. + selectedSum: + type: number + format: double + description: Amount of selected offers in the aggregation. + rejectedCount: + type: integer + description: Number of rejected offers in the aggregation. + rejectedSum: + type: number + format: double + description: Amount of rejected offers in the aggregation. + unknownCount: + type: integer + description: >- + Number of offers in the aggregation which are not selected nor + rejected. + unknownSum: + type: number + format: double + description: >- + Amount of offers in the aggregation which are not selected nor + rejected. + ReportDeclinedTransactions: + type: object + properties: + data: + type: array + items: + type: object + properties: + message: + description: >- + Description or message related to the declined transactions + report. + type: string + example: error + count: + description: Number of declined transactions. + type: integer + example: 500 + percentage: + description: Percentage of declined transactions. + type: number + format: double + example: 25 + ReportDisputes: + type: object + properties: + data: + type: array + description: Disputes data. + items: + type: object + properties: + aggregationValue: + description: >- + Aggregation field value. + + This value is defined in the `aggregationField` parameter by + using a query. + + For example, this value could be: website or country. + type: string + countVisa: + description: Total number of disputed Visa transactions. + type: integer + ratioCountVisa: + description: >- + Ratio of the total number of disputed Visa transactions versus + the total number of settled Visa transactions. + type: number + format: double + ratioAmountVisa: + description: >- + Ratio of disputed Visa transaction amounts versus settled Visa + transaction amounts. + type: number + format: double + countMastercard: + description: Total number of disputed Mastercard transactions. + type: integer + ratioCountMastercard: + description: >- + Ratio of the total number of disputed Mastercard transactions + versus the total number of settled Mastercard transactions. + type: number + format: double + ratioAmountMastercard: + description: >- + Ratio of disputed Mastercard transaction amounts versus + settled Mastercard transaction amounts. + type: number + format: double + ReportEventsTriggeredSummary: + type: object + properties: + data: + type: array + description: Event data. + items: + type: object + properties: + eventName: + description: Name of the system event. + type: string + enum: + - dispute-created + - gateway-account-requested + - transaction-processed + - subscription-canceled + - subscription-renewed + - payment-card-expired + - payment-declined + - transaction-process-requested + - risk-score-changed + count: + description: Total number of times this event is triggered. + type: integer + ReportRulesMatchedSummary: + type: object + properties: + data: + description: Matched rule data. + type: array + items: + type: object + properties: + rule: + description: Name of the matched rule. + type: string + count: + description: Total number of times this rule is matched. + type: integer + approvalRate: + description: Rate of transaction approval by rule. + type: number + format: double + FutureRenewals: + type: object + properties: + data: + description: Future renewals data. + type: array + items: + type: object + description: Contains an aggregation. + readOnly: true + properties: + date: + type: string + description: Date in `YYYY-MM` format. + sum: + type: number + format: double + description: >- + Total amount of future renewals in the user's reporting + currency. + plansCount: + type: array + description: Plan within the aggregation. + items: + type: object + readOnly: true + properties: + planId: + type: string + description: ID of the plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + count: + type: integer + description: Total number of future renewals of a plan. + ReportJournal: + type: object + properties: + aggregationField: + type: string + description: Report data grouped by aggregation field. + enum: + - product.accountingCode + - product.id + - plan.id + currency: + $ref: '#/components/schemas/CurrencyCode' + bookedFrom: + type: string + description: >- + Year and month when revenue is booked. + + If this value is omitted, booked revenue is recorded from the first + booked amount. + pattern: '^\d{4}-\d{2}$' + example: 2022-01 + bookedTo: + type: string + description: >- + Year and month when revenue is booked until. + + If this value is omitted, booked revenue is recorded until the most + recently booked amount. + pattern: '^\d{4}-\d{2}$' + example: 2022-01 + recognizedAt: + type: string + description: Year and month when revenue is recognized. + pattern: '^\d{4}-\d{2}$' + example: 2022-01 + data: + description: Revenue data. + type: array + items: + type: object + properties: + aggregationValue: + description: Aggregation field value. + type: string + bookedMonth: + description: Month when revenue is booked. + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-02 + bookedAmount: + description: Revenue amount which is booked for recognition. + type: number + format: double + example: 25 + recognizedAmount: + description: Amount of recognized revenue. + type: number + format: double + example: 20 + remainingAmount: + description: Remaining revenue amount to be recognized. + type: number + format: double + example: 5 + ReportKycRejections: + type: object + properties: + data: + description: Rejection data. + type: array + items: + type: object + properties: + documentType: + $ref: '#/components/schemas/KycDocumentTypes' + rejectionReasons: + type: array + description: Rejection reasons. + items: + type: object + properties: + rejectionReason: + $ref: '#/components/schemas/KycDocumentRejectionReasonTypes' + count: + type: integer + format: integer + minimum: 0 + description: Total number of related KYC documents. + KycDocumentRequestStatuses: + description: Reason the document is rejected. + type: string + enum: + - abandoned + - expired + - failed + - fulfilled + - pending-review + ReportKycRequests: + type: object + properties: + data: + description: Request data. + type: array + items: + type: object + properties: + rejectionReason: + $ref: '#/components/schemas/KycDocumentRequestStatuses' + count: + type: integer + format: integer + minimum: 0 + description: Number of related KYC requests. + ReportMonthlyRecurringRevenue: + type: object + properties: + data: + description: Monthly Recurring Revenue (MRR) data. + type: array + items: + type: object + properties: + period: + description: Revenue month. + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-06 + total: + description: Total Monthly Recurring Revenue (MRR) amount. + type: number + format: double + example: 245 + breakdown: + description: >- + MRR categories are described below. + + The difference between the current period MRR and the previous + period MRR is equal to: + + `new` + `reactivation` + `expansion` - `contraction` - + `churned`. + type: object + properties: + new: + description: |- + Occurs when new customers sign up. + New MRR is the amount of the new customer's subscription. + + Example: A new customer signs up to a $40 per month plan. + This is a new MRR amount of $40. + type: number + format: double + example: 40 + reactivation: + description: >- + Occurs when a customer stops being a customer for a period + but later signs up again. + + Reactivation MRR is the amount of the customer's new + subscription. + + + Example: A customer cancels their subscription, but signs + up again and purchases a $25 per month plan. + + This is a reactivation MRR amount of $25. + type: number + format: double + example: 25 + churned: + description: >- + Occurs when a subscription is churned. + + Churn occurs when the paid service period on a + subscription order expires. + + Churned MRR is the amount that is lost as a result of the + churned subscription. + + + Example: When a subscription is churned, the value of a + subscription goes from $80 per month to $0 per month. + + This is a churned MRR amount of $80. + type: number + format: double + example: 80 + contraction: + description: >- + Occurs when an existing customer changes items which + result in a smaller MRR. + + Contraction MRR is the amount before the change minus the + amount and after the change. + + + Example: A customer downgrades from a plan that is $100 + per month to plan that is $80 per month. + + This is a contraction MRR amount of $20. + type: number + format: double + example: 20 + expansion: + description: >- + Occurs when existing customers change items which result + in a bigger MRR. + + Expansion MRR is the amount after the change minus the + amount and before the change. + + + Example: A customer upgrades from a plan that is $40 per + month to a plan that is $100 per month. + + This is an expansion MRR amount of $60. + type: number + format: double + example: 60 + RenewalSales: + type: object + properties: + data: + description: Renewal sales data. + type: array + items: + type: object + description: Contains an aggregation. + readOnly: true + properties: + aggregationValue: + type: string + description: Date in `YYYY-MM` format. + newSales: + type: number + format: double + description: Total amount of new sales. + newRefunds: + type: number + format: double + description: Total amount of new refunds. + renewalSales: + type: number + format: double + description: Total amount of renewal sales. + renewalRefunds: + type: number + format: double + description: Total amount of renewal refunds. + ReportRetentionPercentage: + type: object + properties: + data: + description: Retention data. + type: array + items: + type: object + description: Contains an aggregation. + readOnly: true + properties: + aggregationValue: + type: string + description: >- + Value by which the report provides retention periods and + percentages. + + + Date values are displayed as follows: + + + - Day: `YYYY-MM-DD`; + + - Month: `YYYY-MM`; + + - Quarter: `YYYY-MM`/`YYYY-MM`; + + - Year: `YYYY`. + subscriptionsCount: + type: integer + description: Number of subscriptions created within the aggregation. + periods: + description: Periods of the specified aggregation. + type: array + items: + type: object + properties: + period: + type: integer + description: >- + Retention period number. + + This value is measured from the beginning boundary of + the aggregation. + + Periods are numbered as follows: + - `0`: First period. + - `1`: Second period. + - `2`: Third period, and so on. + retentionRatio: + type: number + format: double + description: >- + Ratio percentage of active, non-cancelled, subscriptions + in the retention period versus all subscriptions created + in the aggregation period. + canceledSubscriptionsCount: + type: integer + description: >- + Number of canceled subscriptions within the specified + retention period. + ReportRetentionValue: + type: object + properties: + data: + description: Retention value data. + type: array + items: + type: object + description: Contains an aggregation. + readOnly: true + properties: + aggregationValue: + type: string + description: >- + Value by which the report provides retention periods and + values. + + Date values are displayed as follows: + + + - Day: `YYYY-MM-DD`. + + - Month: `YYYY-MM`. + + - Quarter: `YYYY-MM`/`YYYY-MM`. + + - Year: `YYYY`. + customersCount: + type: integer + description: >- + Number of customers within the aggregation who make their + first payment. + periods: + description: Periods of the specified aggregation. + type: array + items: + type: object + properties: + period: + type: integer + description: >- + Retention period number. + + This value is measured from the beginning boundary of + the aggregation. + - `0`: First period. + - `1`: Second period. + - `2`: Third period, and so on. + retentionAverage: + type: number + format: double + description: >- + Summary amount of all transactions for all periods up to + the current period divided by the aggregation customer + number. + transactionsCount: + type: integer + description: >- + Number of transactions that occurred within the + retention period. + + For example, in 3 rebills. + transactionsValue: + type: number + format: double + description: >- + Total value of transactions. + + This value is calculated based on: `income transactions` + minus `loss transactions`. + ReportRevenueWaterfall: + type: array + items: + type: object + properties: + issuedMonth: + description: Month when the revenue invoice is issued. + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-02 + booked: + description: Month when the revenue amount is booked. + type: number + format: double + example: 25 + recognized: + description: >- + Amount of recognized revenue amount up to, and including, the + `recognizedTo` month. + type: number + format: double + example: 20 + remaining: + description: >- + Amount of revenue that remains to be recognized after the + `recognizedTo` month. + type: number + format: double + example: 5 + waterfall: + type: array + description: Recognized revenue waterfall for each month. + items: + type: object + properties: + recognizedMonth: + description: Month when revenue is recognized. + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-04 + amount: + description: Amount of revenue recognized at the `recognizedMonth`. + type: number + format: double + example: + - recognizedMonth: 2022-02 + amount: 4.4 + - recognizedMonth: 2022-03 + amount: 8.33 + - recognizedMonth: 2022-04 + amount: 7.27 + RevenueEntry: + type: object + readOnly: true + properties: + customerId: + $ref: '#/components/schemas/CustomerId' + invoiceId: + type: string + description: >- + ID of the invoice that contains the item which generated the + revenue. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + invoiceItemId: + type: string + description: ID of the invoice item which generated the revenue. + maxLength: 50 + example: ii_0YVFDEQS2KCFTBN9HXWJFY55GV + productId: + description: ID of the product related to the invoice item. + type: + - string + - 'null' + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + planId: + description: ID of the plan related to the invoice item. + type: + - string + - 'null' + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + accountingCode: + description: Product accounting code. + type: string + example: '4010' + currency: + $ref: '#/components/schemas/CurrencyCode' + status: + description: Entry recognition status. + readOnly: true + type: string + enum: + - scheduled + - recognized + x-enumDescriptions: + scheduled: The amount is recognized at the end of the month. + recognized: The amount is recognized and final. + estimatedAmount: + description: >- + Estimated amount of recognized revenue unless it changes before + recognition time. + type: number + x-type: Money + format: double + recognizedAmount: + description: Recognized amount. + type: number + x-type: Money + format: double + scheduledTime: + description: Date and time when the entry is created. + type: string + format: date-time + readOnly: true + issuedTime: + description: Date and time when the related invoice is issued. + type: string + format: date-time + readOnly: true + recognizedTime: + description: Date and time when the entry is recognized. + type: + - string + - 'null' + format: date-time + readOnly: true + SubscriptionCancellationReport: + type: object + properties: + data: + description: Subscription cancellation data. + type: array + items: + type: object + description: Contains an aggregation. + readOnly: true + properties: + aggregationValue: + type: string + description: Aggregation value. + count: + type: integer + description: Total number of canceled subscriptions. + averageLength: + type: number + format: double + description: >- + Average length of canceled subscription from start to end + within the aggregation in seconds. + medianLength: + type: number + format: double + description: >- + Median length of canceled subscription from start to end + within the aggregation in seconds. + SubscriptionRenewal: + type: object + properties: + data: + description: Subscription renewal data. + type: array + items: + type: object + description: Contains an aggregation. + readOnly: true + properties: + planId: + type: string + description: Plan ID for which subscriptions are counted. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + allRenewalCount: + type: integer + description: Total number of renewed subscriptions. + allDunningCount: + type: integer + description: >- + Total number of dunned subscriptions. + + Dunning is the process of retrying unsuccessful payment + transactions. + abandonedCount: + type: integer + description: Total number of abandoned subscriptions. + paidRenewalCount: + type: integer + description: |- + Total number of paid renewed subscriptions. + To determine the renewal rate, + divide the value of this field by the `allRenewalCount` value. + paidDunningCount: + type: integer + description: |- + Total number of paid dunned subscriptions. + To determine the dunning success rate, + divide the value of this field by the `allDunningCount` value. + refundedRenewalCount: + type: integer + description: |- + Total number of paid renewed subscriptions. + To determine the renewal refund rate, + divide the value of this field by the `allRenewalCount` value. + refundedDunningCount: + type: integer + description: |- + Total number of paid dunned subscriptions. + To determine the dunning refund rate, + divide the value of this field by the `allDunningCount` value. + chargebackRenewalCount: + type: integer + description: |- + Total number of paid renewed subscriptions. + To determine the renewal chargeback rate, + divide the value of this field by the `allRenewalCount` value. + chargebackDunningCount: + type: integer + description: |- + Total number of paid dunned subscriptions. + To determine the dunning chargeback rate, + divide the value of this field by the `allDunningCount` value. + ReportTax: + type: object + properties: + data: + description: Tax data. + type: array + items: + type: object + properties: + country: + description: >- + Country to which the tax is owed, in [ISO Alpha-2 code + format](https://www.iso.org/obp/ui/#search/code/). + type: string + example: US + state: + description: >- + Postal abbreviated state name for the state to which the tax + is owed. + type: string + example: TX + county: + description: Name of county to which the tax is owed. + type: string + example: TRAVIS + city: + description: Name of city to which the tax is owed. + type: string + example: AUSTIN + stateRate: + description: Percentage rate of state tax. + type: number + format: double + stateAmount: + description: Amount of state tax. + type: number + format: double + countyRate: + description: Percentage rate of county tax. + type: number + format: double + countyAmount: + description: Amount of county tax. + type: number + format: double + cityRate: + description: Percentage rate of city tax. + type: number + format: double + cityAmount: + description: Amount of city tax. + type: number + format: double + specialDistrictRate: + description: Percentage rate of special district tax. + type: number + format: double + specialDistrictAmount: + description: Amount of special district tax. + type: number + format: double + taxableSalesAmount: + description: Amount of taxable sales. + type: number + format: double + nontaxableSalesAmount: + description: Amount of nontaxable sales. + type: number + format: double + TimeSeriesTransaction: + type: object + properties: + data: + description: Time series data. + type: array + items: + type: object + description: Contains an aggregation. + readOnly: true + properties: + date: + type: string + description: Date in `YYYY-MM-DD` format . + total: + type: number + format: double + description: Total number of all transactions within the aggregation. + subaggregates: + type: object + description: Subaggregates that have transactions within the range. + properties: + subaggregate: + type: string + description: ID of the subaggregate. + value: + type: number + format: double + description: >- + Total number of all subaggregate transactions within the + aggregation. + ReportDisputeDelays: + type: object + properties: + data: + description: Dispute delay data. + type: array + items: + type: object + properties: + aggregationFieldValue: + description: >- + Aggregation field value. + + This value is defined in the `aggregationField` parameter by + using a query. + + For example, this value could be: website or country. + type: string + 25th: + description: >- + Delay between dispute and transaction time at the 25th + percentile. + type: integer + 50th: + description: >- + Delay between dispute and transaction time at the 50th + percentile. + type: integer + 75th: + description: >- + Delay between dispute and transaction time at the 75th + percentile. + type: integer + ReportTransactions: + type: object + properties: + data: + description: Transaction data. + type: array + readOnly: true + items: + type: object + properties: + aggregationFieldValue: + description: Aggregation field value. + type: string + authApprovedThroughput: + description: Percentage of approved authentication transactions. + type: number + format: double + approvedThroughput: + description: Percentage of approved sale transactions. + type: number + format: double + authApprovalCount: + description: Total number of approved authentication transactions. + type: integer + disputesRate: + description: Percentage of disputed sale and capture transactions. + type: number + format: double + disputesCount: + description: Total number of disputed sale and capture transactions. + type: integer + salesCount: + description: Total number of sales. + type: integer + salesValue: + description: Total sales value. + type: number + format: double + salesAverage: + description: Average sales value. + type: number + format: double + refundsCount: + description: Total number of refunds. + type: integer + refundsValue: + description: Total value of refunds. + type: number + format: double + amount: + description: |- + Amount of revenue. + This value is the result of `salesValue` minus `refundsValue`. + type: number + format: double + count: + description: Total number of transactions. + type: number + format: double + unapprovedCount: + description: Total number of unapproved transactions. + type: number + format: double + SubscriptionSummaryMetrics: + type: object + properties: + currency: + description: Currency code in ISO 4217 format. + minLength: 3 + maxLength: 3 + example: USD + readOnly: true + type: string + invoicedAmount: + description: 'Total amount of all issued, past due, and paid invoices.' + readOnly: true + type: number + format: double + collectedAmount: + description: Total amount of all paid invoices. + readOnly: true + type: number + format: double + invoiceCount: + description: 'Number of issued, past due, or paid invoices.' + readOnly: true + type: integer + StorefrontAccount: + type: object + properties: + id: + description: ID of the account. + readOnly: true + $ref: '#/components/schemas/ResourceId' + websiteId: + readOnly: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + username: + type: string + description: Username associated with the account. + readOnly: true + primaryAddress: + $ref: '#/components/schemas/ContactObject' + paymentToken: + type: string + writeOnly: true + description: >- + Write-only payment token. + + If this value is supplied, it converts into a payment instrument and + set as the `defaultPaymentInstrument`. + + The value of this property overrides the `defaultPaymentInstrument` + if both are supplied. + + This token may only be used once before it expires. + defaultPaymentInstrument: + type: object + description: Default payment instrument information. + required: + - method + properties: + method: + $ref: '#/components/schemas/PaymentMethod' + paymentInstrumentId: + type: string + description: ID of the payment instrument. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + website: + type: object + StorefrontBillingPortal: + type: object + required: + - websiteId + - slug + properties: + id: + type: string + description: ID of the billing portal. + readOnly: true + maxLength: 50 + example: bill_prt_0YV7K5TYV5D1P9ZNKDT39KZC3C + token: + description: Session's token used for authentication. + type: string + readOnly: true + slug: + description: >- + Path segment that is appended to the billing portal URL to help make + it human-readable. + + Example: `https://example.com/billing-portal/{slug}`. + type: string + minLength: 5 + maxLength: 100 + websiteId: + $ref: '#/components/schemas/WebsiteId' + features: + description: Features that can be enabled for the billing portal. + type: object + properties: + authenticateWithPassword: + description: Specifies if a customer can authenticate with a password. + type: boolean + default: true + orderCancel: + description: Specifies if a customer can cancel an order. + type: boolean + default: true + orderAddressEdit: + description: Specifies if a customer can change an order address. + type: boolean + default: true + paymentInstrumentAdd: + description: Specifies if a customer can add a new payment instrument. + type: boolean + default: true + paymentInstrumentUpdate: + description: Specifies if a customer can update their payment instruments. + type: boolean + default: true + paymentInstrumentDeactivate: + description: Specifies if a customer can disable their payment instruments. + type: boolean + default: true + customization: + description: Visual customization options for the billing portal. + type: object + properties: + logoId: + description: ID of the linked file object. + $ref: '#/components/schemas/ResourceId' + colors: + description: Various colors used in the billing portal. + type: object + properties: + primary: + description: Primary color for the billing portal in hexadecimal format. + type: string + maxLength: 6 + default: 0044d4 + secondary: + description: >- + Secondary color for the billing portal in hexadecimal + format. + type: string + maxLength: 6 + default: ffffff + links: + description: URLs that are displayed in the billing portal. + type: object + properties: + refundPolicy: + description: Website refund policy URL. + type: string + format: url + privacyPolicy: + description: Website privacy policy URL. + type: string + format: url + termsOfService: + description: Website terms of service URL. + type: string + format: url + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - logoUrl + StorefrontTransaction: + type: object + description: Transaction information. + properties: + id: + readOnly: true + $ref: '#/components/schemas/TransactionId' + websiteId: + readOnly: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + type: + description: Type of transaction. + type: string + x-basic: true + readOnly: true + enum: + - 3ds-authentication + - authorize + - capture + - credit + - refund + - sale + - setup + - void + status: + description: Status of the transaction. + type: string + readOnly: true + enum: + - completed + - conn-error + - disputed + - never-sent + - offsite + - partially-refunded + - pending + - refunded + - sending + - timeout + - voided + - waiting-approval + - waiting-capture + - waiting-gateway + - waiting-refund + result: + description: Result of the transaction. + type: string + x-basic: true + readOnly: true + enum: + - abandoned + - approved + - canceled + - declined + - unknown + amount: + x-type: Money + x-sortable: true + x-basic: true + description: Total amount of the transaction. + type: number + format: double + readOnly: true + currency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + purchaseAmount: + description: >- + Amount by which the purchase is completed. + + If an adjustment occurs, the purchased amount may differ from the + requested amount. + type: number + format: double + x-type: Money + x-currency-field: purchaseCurrency + x-sortable: true + readOnly: true + purchaseCurrency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + requestAmount: + description: |- + Amount of the payment request. + If an adjustment occurs, + the purchase amount may differ from the billing amount. + type: number + x-type: Money + x-currency-field: requestCurrency + format: double + readOnly: true + requestCurrency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + parentTransactionId: + description: ID of the parent transaction. + $ref: '#/components/schemas/TransactionId' + childTransactions: + description: IDs of child transactions. + readOnly: true + type: array + items: + $ref: '#/components/schemas/ResourceId' + invoiceIds: + description: Related invoice IDs. + readOnly: true + type: array + items: + $ref: '#/components/schemas/ResourceId' + orderIds: + description: Subscription IDs of invoices that are related to the transaction. + readOnly: true + type: array + items: + $ref: '#/components/schemas/ResourceId' + planIds: + description: Plan IDs of orders that are related to the transaction. + readOnly: true + type: array + items: + $ref: '#/components/schemas/ResourceId' + isRebill: + description: >- + Specifies if the transaction is one of a number of recurring + payments in a subscription, excluding trials or setup fees. + type: boolean + readOnly: true + rebillNumber: + description: >- + Rebill number of the transaction. + + A rebill number is the number of recurring payments in a + subscription, excluding trials or setup fees. + type: integer + readOnly: true + x-sortable: true + billingAddress: + description: Billing address. + $ref: '#/components/schemas/ContactObject' + redirectUrl: + description: >- + URL where the end-user is redirected to when an offsite transaction + is completed. + + The default value is the website URL. + type: + - string + - 'null' + format: uri + retryNumber: + type: integer + readOnly: true + description: Position of the transaction in the sequence of retries. + x-sortable: true + isRetry: + type: boolean + readOnly: true + description: Specifies if a transaction is a retry. + billingDescriptor: + type: + - string + - 'null' + readOnly: true + description: >- + Billing descriptor that appears on the periodic billing statement. + + For a credit card statement, this field commonly contains 12 or + fewer characters. + description: + type: string + description: Description of the payment. + maxLength: 255 + requestId: + description: >- + Request ID of the transaction. This ID must be unique within a 24 + hour period. + + Use this field to prevent duplicated transactions. + type: string + x-sortable: true + hasAmountAdjustment: + description: Specifies if the transaction has amount adjustment. + type: boolean + readOnly: true + gatewayName: + readOnly: true + description: >- + Name of the payment gateway that processed, or is selected to + process, the transaction. + + This value is only available after a gateway is selected for the + transaction. + x-label: Gateway + x-basic: true + allOf: + - $ref: '#/components/schemas/GatewayName' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + processedTime: + description: Date and time when the transaction is processed. + x-sortable: true + x-basic: true + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + paymentInstrument: + type: object + description: Default payment instrument information. + required: + - method + properties: + method: + $ref: '#/components/schemas/PaymentMethod' + paymentInstrumentId: + type: string + description: ID of the payment instrument. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + approvalUrl: + description: >- + URL to redirect the end-customer when transaction `status` is + `waiting-approval` or `offsite`. + type: string + format: uri + token: + description: >- + Session token that is used for authentication. This token provides + access to created orders, invoices, and transactions. + type: string + readOnly: true + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - approvalUrl + StorefrontCheckoutForm: + type: object + required: + - websiteId + - plans + properties: + id: + type: string + description: ID of the checkout form. + readOnly: true + maxLength: 50 + example: chkt_frm_0YV8XZ6174C2MBS5011SAZNMBP + websiteId: + $ref: '#/components/schemas/WebsiteId' + customDomain: + description: Custom domain for the checkout form. + type: + - string + - 'null' + maxLength: 255 + plans: + type: array + description: >- + List of plans that are applied to a customer order by default. + + Plans describe how the customer must pay for products. For more + information, see + [Plans](https://www.rebilly.com/docs/dev-docs/api/tag/Plans/). + minItems: 1 + items: + $ref: '#/components/schemas/CheckoutFormPlan' + addonPlans: + description: >- + List of add-on plans that are displayed to the customer on the + payment screen. + + Add-ons are plans that the customer has not already subscribed to. + + + The customer selects whether to add an add-on plan to their current + order. + default: [] + type: array + items: + $ref: '#/components/schemas/CheckoutFormPlan' + bumpPlans: + description: >- + List of bump plans that are displayed to the customer on the payment + screen. + + Use bump plans to offer purchase bonuses, discounts, and deals to + the customer. + + + The customer selects whether to purchase bump plans, + + or to use the plans that are specified in their current order. + default: [] + type: array + items: + $ref: '#/components/schemas/CheckoutFormPlan' + accountsEnabled: + description: >- + Specifies if the account is enabled. + + If this value is `true`, the customer can sign-up and sign-in using + the checkout form. + type: boolean + default: false + couponsEnabled: + description: >- + Specifies if coupons are enabled in the checkout form. + + If this value is `true`, the customer can use coupons in the + checkout form. + + Use coupons to reward customers, generate sales, or to test new + pricing strategies. + type: boolean + default: false + purchaseLimit: + description: >- + Limits the number of purchases that can be made using a specific + checkout form. + + If a purchase limit value is set, each purchase decreases this + value. + + When the purchases limit value reaches zero, the checkout form + becomes inactive. + type: + - integer + - 'null' + minimum: 0 + default: null + paymentMethods: + description: |- + List of available payment methods. + Payment methods must have at least one active gateway account. + If not specified, all available payment methods are displayed. + type: array + items: + $ref: '#/components/schemas/PaymentMethod' + customization: + description: Visual customization options for the checkout form. + type: object + properties: + logoId: + description: ID of the linked file object. + $ref: '#/components/schemas/ResourceId' + summary: + description: Summary text. + type: string + buttonText: + description: >- + Button text. Use the `{{amount}}` placeholder to display the + checkout form total. + type: string + default: 'Pay {{amount}}' + colors: + description: Primary color used in the checkout form and button text. + type: object + properties: + primary: + description: Primary color for the checkout form in hexadecimal format. + type: string + maxLength: 6 + default: 0044d4 + buttonText: + description: >- + Button text color for the checkout form in hexadecimal + format. + type: string + maxLength: 6 + default: ffffff + links: + description: URLs that are displayed on the checkout form. + type: object + properties: + refundPolicy: + description: Website refund policy URL. + type: string + format: url + privacyPolicy: + description: Website privacy policy URL. + type: string + format: url + termsOfService: + description: Website terms of service URL. + type: string + format: url + tracking: + description: Tracking system IDs. + type: object + properties: + googleAnalytics: + description: Google Analytics tracking ID. + type: string + example: UA-XXXXX-YY + googleTagManager: + description: Google Tag Manager tracking ID. + type: string + example: GTM-XXXXX + gtagJs: + description: >- + Google Analytics tracking ID. + + This value is used by Google Global Site Tag (gtag.js) + service. + type: string + example: UA-XXXXX-YY + facebookPixel: + description: Facebook Pixel tracking ID. + type: string + example: '1234567890' + segmentAnalytics: + description: Segment Analytics tracking ID. + type: string + example: '1234567890' + heapIo: + description: Heap.io tracking ID. + type: string + example: '1234567890' + requiredAdditionalFields: + description: List of required fields. + type: array + items: + type: string + example: + - information.companyName + - information.phoneNumber + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + token: + description: Session token that is used for authentication. + type: string + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontInvoice: + properties: + id: + type: string + description: ID of the invoice. + readOnly: true + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + websiteId: + $ref: '#/components/schemas/WebsiteId' + invoiceNumber: + description: >- + Auto-incrementing number based on the sequence of invoices for any + particular customer. + readOnly: true + type: integer + x-basic: true + subscriptionId: + type: string + description: |- + ID of the related subscription order, if available. + This field is `null` if there are no related subscription orders. + readOnly: true + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + currency: + x-sortable: true + x-basic: true + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Amount of the invoice. + type: number + x-type: Money + x-sortable: true + x-basic: true + format: double + readOnly: true + amountDue: + description: Amount that is due on the invoice. + type: number + x-type: Money + x-sortable: true + format: double + readOnly: true + subtotalAmount: + description: Subtotal amount of the invoice. + type: number + x-type: Money + format: double + readOnly: true + discountAmount: + description: Discount amount that is applied to the invoice. + type: number + x-type: Money + format: double + readOnly: true + shipping: + $ref: '#/components/schemas/Shipping' + tax: + $ref: '#/components/schemas/Taxes' + billingAddress: + description: Billing address of the invoice. + $ref: '#/components/schemas/ContactObject' + deliveryAddress: + description: Delivery address of the invoice. + $ref: '#/components/schemas/ContactObject' + poNumber: + description: Purchase order number that is displayed on the invoice. + type: + - string + - 'null' + example: PO123456 + notes: + description: Notes for the customer that are displayed on the invoice. + type: string + items: + type: array + description: Invoice items array. + readOnly: true + items: + $ref: '#/components/schemas/InvoiceItem' + discounts: + type: array + description: Discounts applied. + readOnly: true + items: + type: object + readOnly: true + properties: + couponId: + type: string + description: ID of the coupon. + maxLength: 50 + example: cpn_0YVCNKF81GD778N4YNVGDJK558 + redemptionId: + description: ID of the redemption. + $ref: '#/components/schemas/ResourceId' + amount: + description: Total amount discounted by this coupon. + type: number + format: double + description: + type: string + description: Description of the discount. + context: + $ref: '#/components/schemas/DiscountContext' + autopayScheduledTime: + description: Date and time when an automatic payment (autopay) is scheduled. + type: string + x-sortable: true + format: date-time + autopayRetryNumber: + description: >- + Number of times that an automatic payment (autopay) has been + attempted on an invoice. + readOnly: true + type: integer + x-sortable: true + minimum: 0 + default: 0 + status: + type: string + description: Status of the invoice. + x-basic: true + readOnly: true + enum: + - draft + - unpaid + - paid + - partially-paid + - past-due + - abandoned + - voided + - partially-refunded + - refunded + - disputed + delinquentCollectionPeriod: + type: integer + description: >- + Length of time, in days, between when the invoice is due and when + the invoice is paid. + x-sortable: true + readOnly: true + collectionPeriod: + type: integer + x-sortable: true + description: >- + Length of time, in days, between when the invoice is issued and when + the invoice is paid. + readOnly: true + abandonedTime: + description: Date and time when the invoice is abandoned. + x-sortable: true + $ref: '#/components/schemas/ServerTimestamp' + voidedTime: + description: Date and time when the invoice is voided. + x-sortable: true + $ref: '#/components/schemas/ServerTimestamp' + paidTime: + x-label: Payment Date + x-sortable: true + x-basic: true + description: Date and time when the invoice is paid. + $ref: '#/components/schemas/ServerTimestamp' + dueTime: + description: Date and time when the invoice is due for payment. + type: string + x-sortable: true + format: date-time + issuedTime: + description: Date and time when the invoice is issued. + x-label: Date Issued + x-sortable: true + x-basic: true + $ref: '#/components/schemas/ServerTimestamp' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + paymentFormUrl: + type: string + readOnly: true + format: url + description: |- + URL where the customer is redirected to pay the invoice + using one of the methods which are available to the customer. + This is an alternative to creating a new transaction with empty + `methods`. + transactions: + type: array + description: Invoice transactions array. + readOnly: true + items: + $ref: '#/components/schemas/StorefrontTransaction' + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontProofOfIdentityKycDocument: + type: object + required: + - documentType + - status + properties: + id: + readOnly: true + $ref: '#/components/schemas/ResourceId' + fileIds: + description: >- + IDs of linked file objects. + + + Uploaded `identity-proof` files must have the following tags + attached to be used for KYC purposes: + + `['kyc', 'id-front']`, `['kyc', 'id-back']`, `['kyc', + 'face-proof']`. + type: array + items: + $ref: '#/components/schemas/ResourceId' + documentType: + description: >- + Document type submitted for validation. + + Only the `identity-proof` and `address-proof` types are analyzed + automatically. + $ref: '#/components/schemas/KycDocumentTypes' + documentSubtype: + description: Document subtype submitted for validation. + $ref: '#/components/schemas/KycDocumentSubtypes' + status: + description: Status of the validation. + type: string + readOnly: true + enum: + - pending + - in-progress + - accepted + - rejected + - archived + x-enumDescriptions: + pending: Waiting to be reviewed or analyzed. + in-progress: Being analyzed by the Rebilly AI. + accepted: Accepted by AI or a human. + rejected: Rejected by AI or a human. + archived: Archived by the Rebilly AI. + rejectionReason: + $ref: '#/components/schemas/KycDocumentRejection' + requestId: + readOnly: true + type: + - string + - 'null' + description: ID of the KYC request. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + processedTime: + description: Date and time when the KYC document is processed. + type: string + format: date-time + readOnly: true + documentMatches: + type: object + readOnly: true + description: Document matches. + properties: + score: + description: >- + Calculated score that represents the percentage of confidence + that this ID represents the customer. + type: number + format: double + example: 0.75 + data: + $ref: '#/components/schemas/KycIdentityMatches' + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontProofOfAddressKycDocument: + type: object + required: + - documentType + - status + properties: + id: + readOnly: true + $ref: '#/components/schemas/ResourceId' + fileIds: + description: >- + IDs of linked file objects. + + + Uploaded `identity-proof` files must have the following tags + attached to be used for KYC purposes: + + `['kyc', 'id-front']`, `['kyc', 'id-back']`, `['kyc', + 'face-proof']`. + type: array + items: + $ref: '#/components/schemas/ResourceId' + documentType: + description: >- + Document type submitted for validation. + + Only the `identity-proof` and `address-proof` types are analyzed + automatically. + $ref: '#/components/schemas/KycDocumentTypes' + documentSubtype: + description: Document subtype submitted for validation. + $ref: '#/components/schemas/KycDocumentSubtypes' + status: + description: Status of the validation. + type: string + readOnly: true + enum: + - pending + - in-progress + - accepted + - rejected + - archived + x-enumDescriptions: + pending: Waiting to be reviewed or analyzed. + in-progress: Being analyzed by the Rebilly AI. + accepted: Accepted by AI or a human. + rejected: Rejected by AI or a human. + archived: Archived by the Rebilly AI. + rejectionReason: + $ref: '#/components/schemas/KycDocumentRejection' + requestId: + readOnly: true + type: + - string + - 'null' + description: ID of the KYC request. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + processedTime: + description: Date and time when the KYC document is processed. + type: string + format: date-time + readOnly: true + documentMatches: + description: Document matches. + type: object + readOnly: true + properties: + score: + description: >- + Calculated score that represents the percentage of confidence + that this proof of address represents the customer. + type: number + format: double + example: 0.75 + data: + $ref: '#/components/schemas/KycAddressMatches' + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontProofOfFundsKycDocument: + type: object + required: + - documentType + - status + properties: + id: + readOnly: true + $ref: '#/components/schemas/ResourceId' + fileIds: + description: >- + IDs of linked file objects. + + + Uploaded `identity-proof` files must have the following tags + attached to be used for KYC purposes: + + `['kyc', 'id-front']`, `['kyc', 'id-back']`, `['kyc', + 'face-proof']`. + type: array + items: + $ref: '#/components/schemas/ResourceId' + documentType: + description: >- + Document type submitted for validation. + + Only the `identity-proof` and `address-proof` types are analyzed + automatically. + $ref: '#/components/schemas/KycDocumentTypes' + documentSubtype: + description: Document subtype submitted for validation. + $ref: '#/components/schemas/KycDocumentSubtypes' + status: + description: Status of the validation. + type: string + readOnly: true + enum: + - pending + - in-progress + - accepted + - rejected + - archived + x-enumDescriptions: + pending: Waiting to be reviewed or analyzed. + in-progress: Being analyzed by the Rebilly AI. + accepted: Accepted by AI or a human. + rejected: Rejected by AI or a human. + archived: Archived by the Rebilly AI. + rejectionReason: + $ref: '#/components/schemas/KycDocumentRejection' + requestId: + readOnly: true + type: + - string + - 'null' + description: ID of the KYC request. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + processedTime: + description: Date and time when the KYC document is processed. + type: string + format: date-time + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontProofOfPurchaseKycDocument: + type: object + required: + - documentType + - status + properties: + id: + readOnly: true + $ref: '#/components/schemas/ResourceId' + fileIds: + description: >- + IDs of linked file objects. + + + Uploaded `identity-proof` files must have the following tags + attached to be used for KYC purposes: + + `['kyc', 'id-front']`, `['kyc', 'id-back']`, `['kyc', + 'face-proof']`. + type: array + items: + $ref: '#/components/schemas/ResourceId' + documentType: + description: >- + Document type submitted for validation. + + Only the `identity-proof` and `address-proof` types are analyzed + automatically. + $ref: '#/components/schemas/KycDocumentTypes' + documentSubtype: + description: Document subtype submitted for validation. + $ref: '#/components/schemas/KycDocumentSubtypes' + status: + description: Status of the validation. + type: string + readOnly: true + enum: + - pending + - in-progress + - accepted + - rejected + - archived + x-enumDescriptions: + pending: Waiting to be reviewed or analyzed. + in-progress: Being analyzed by the Rebilly AI. + accepted: Accepted by AI or a human. + rejected: Rejected by AI or a human. + archived: Archived by the Rebilly AI. + rejectionReason: + $ref: '#/components/schemas/KycDocumentRejection' + requestId: + readOnly: true + type: + - string + - 'null' + description: ID of the KYC request. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + processedTime: + description: Date and time when the KYC document is processed. + type: string + format: date-time + readOnly: true + documentMatches: + type: object + readOnly: true + description: Document matches. + properties: + data: + $ref: '#/components/schemas/PurchaseMatches' + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontProofOfCreditFileKycDocument: + type: object + required: + - documentType + - status + properties: + id: + readOnly: true + $ref: '#/components/schemas/ResourceId' + fileIds: + description: >- + IDs of linked file objects. + + + Uploaded `identity-proof` files must have the following tags + attached to be used for KYC purposes: + + `['kyc', 'id-front']`, `['kyc', 'id-back']`, `['kyc', + 'face-proof']`. + type: array + items: + $ref: '#/components/schemas/ResourceId' + documentType: + description: >- + Document type submitted for validation. + + Only the `identity-proof` and `address-proof` types are analyzed + automatically. + $ref: '#/components/schemas/KycDocumentTypes' + documentSubtype: + description: Document subtype submitted for validation. + $ref: '#/components/schemas/KycDocumentSubtypes' + status: + description: Status of the validation. + type: string + readOnly: true + enum: + - pending + - in-progress + - accepted + - rejected + - archived + x-enumDescriptions: + pending: Waiting to be reviewed or analyzed. + in-progress: Being analyzed by the Rebilly AI. + accepted: Accepted by AI or a human. + rejected: Rejected by AI or a human. + archived: Archived by the Rebilly AI. + rejectionReason: + $ref: '#/components/schemas/KycDocumentRejection' + requestId: + readOnly: true + type: + - string + - 'null' + description: ID of the KYC request. + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + processedTime: + description: Date and time when the KYC document is processed. + type: string + format: date-time + readOnly: true + documentMatches: + type: object + readOnly: true + description: Document matches. + properties: + data: + $ref: '#/components/schemas/CreditFileMatches' + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontKycDocument: + oneOf: + - $ref: '#/components/schemas/StorefrontProofOfIdentityKycDocument' + - $ref: '#/components/schemas/StorefrontProofOfAddressKycDocument' + - $ref: '#/components/schemas/StorefrontProofOfFundsKycDocument' + - $ref: '#/components/schemas/StorefrontProofOfPurchaseKycDocument' + - $ref: '#/components/schemas/StorefrontProofOfCreditFileKycDocument' + discriminator: + propertyName: documentType + mapping: + identity-proof: '#/components/schemas/StorefrontProofOfIdentityKycDocument' + address-proof: '#/components/schemas/StorefrontProofOfAddressKycDocument' + funds-proof: '#/components/schemas/StorefrontProofOfFundsKycDocument' + purchase-proof: '#/components/schemas/StorefrontProofOfPurchaseKycDocument' + credit-file-proof: '#/components/schemas/StorefrontProofOfCreditFileKycDocument' + StorefrontKycRequest: + type: object + description: KYC request information. + properties: + id: + type: string + readOnly: true + description: ID of the KYC request. + maxLength: 50 + example: kyc_req_0YV7JMJ3DBCGRBR7K9D4HVGPP5 + documents: + type: array + description: Documents to request from the customer. + minItems: 1 + items: + $ref: '#/components/schemas/KycRequestDocument' + status: + description: Status of the request. + type: string + readOnly: true + enum: + - gathering + - attempted + - partial + - pending-review + - fulfilled + - failed + - abandoned + - expired + x-enumDescriptions: + gathering: |- + No documents have been provided yet. + This is a temporary state. + attempted: >- + At least one document has been provided but none were assigned the + `accepted` status. + + This is a temporary state. + partial: >- + At least one requested document has the `accepted` status, + + but not all requested documents have been assigned the `accepted` + status. + + This is a temporary state. + pending-review: >- + At least one requested document has the `pending` status, + + and no requested documents have been assigned the `accepted` + status. + + This is a temporary state, until the document is reviewed, + + or another `accepted` document is provided. + fulfilled: >- + All requested documents are provided and have been assigned the + `accepted` status. + + This is a permanent state. + failed: >- + At least one requested document has exhausted all attempts, + + and has not been assigned a `accepted`, `pending`, or + `in-progress` status. + + This is a permanent state. + abandoned: |- + One or more documents provided but the request expired. + This is a permanent state. + expired: |- + No documents were provided and the request expired. + This is a permanent state. + redirectUrl: + description: >- + URL where the customer is redirected when a KYC document upload is + complete. + + When the customer is redirected, + + Rebilly appends an `info` query parameter that has one of the + following values: + - `back`: Customer clicked the `back to website` link. + - `token_expired`: Customer's token expired. + - `success`: Customer uploaded KYC documents that have been analyzed. + - `manual`: Customer uploaded KYC documents that require a manual review. + This is because the analyzer rejected the documents or could not process them. + - `partial`: Some of the customer's KYC documents have been analyzed, + but other documents have not. + For example, this may occur when a proof of address document is analyzed but proof of ID is not. + + Example: `https://example.com?info=success`. + type: string + format: uri + expirationTime: + description: |- + Date and time when the request expires. + The default value is one hour in the future. + type: string + format: date-time + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - documents + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + documents: + type: array + StorefrontKycLivenessSession: + type: object + description: KYC liveness session information. + required: + - customerId + properties: + id: + type: string + readOnly: true + description: ID of the KYC liveness session. + maxLength: 50 + example: kyc_liv_0YV7JMJ3DBCGRBR7K9D4HVGPP5 + customerId: + $ref: '#/components/schemas/CustomerId' + readOnly: true + kycRequestId: + type: string + description: ID of the KYC request that the liveness session is associated with. + maxLength: 50 + example: kyc_req_0YV7JMJ3DBCGRBR7K9D4HVGPP5 + sessionId: + type: string + readOnly: true + description: Session ID of the KYC liveness session in the third-party service. + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + status: + description: Status of the session. + type: string + readOnly: true + enum: + - pending + - completed + - failed + - abandoned + x-enumDescriptions: + pending: |- + Session is created, but not processed yet. + This is a temporary state. + completed: |- + Session is completed. + This is a permanent state. + failed: |- + Session has failed. + This is a permanent state. + abandoned: |- + Session was not completed and timed out. + This is a permanent state. + referenceImage: + type: + - string + - 'null' + readOnly: true + description: URL of the reference image. + format: uri + example: 'https://www.rebilly.com/blog/header-images/handle_chargebacks.png' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - kycRequest + StorefrontCustomerJWT: + type: object + properties: + id: + type: string + description: ID of the session. + readOnly: true + maxLength: 50 + example: jwt_0YV7DEJX80CDRAKVTV478ZNJDR + token: + description: Session's token used for authentication. + type: string + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + expiredTime: + description: Session expired time. Defaults to one hour. + type: string + format: date-time + StorefrontSubscriptionOrder: + type: object + properties: + id: + type: string + description: ID of the order. + readOnly: true + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + orderType: + description: |- + Specifies the type of order. + An order may be a subscription or a one-time purchase. + type: string + enum: + - subscription-order + - one-time-order + default: subscription-order + billingStatus: + description: >- + Billing status of the most recent invoice. + + This value may help you to determine if you should change the + service status of the service, + + such as suspending the service. + type: string + readOnly: true + enum: + - draft + - unpaid + - past-due + - abandoned + - paid + - voided + - refunded + - disputed + - partially-refunded + - partially-paid + websiteId: + x-sortable: true + x-basic: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + currency: + description: Currency of the order. + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + initialInvoiceId: + type: string + description: ID of the initial invoice. + readOnly: true + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + recentInvoiceId: + type: string + description: |- + ID of the most recently issued invoice. + The invoice might not be `paid` yet. + readOnly: true + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + items: + description: Details of items in the order. + type: array + minItems: 1 + items: + $ref: '#/components/schemas/OrderItem' + deliveryAddress: + description: Delivery address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + billingAddress: + description: Billing address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + activationTime: + description: Date and time when the order is activated. + x-sortable: true + $ref: '#/components/schemas/ServerTimestamp' + voidTime: + description: Date and time when the order is voided. + $ref: '#/components/schemas/ServerTimestamp' + abandonTime: + type: + - string + - 'null' + description: >- + Date and time when the pending order is automatically abandoned. + + If this value is not passed during order creation, + + a [pending order + TTL](https://all-rebilly.redoc.ly/tag/Organizations/operation/PatchOrganization/#!path=settings/billing/pendingOrderTtl&t=request) + setting is used to calculate the value. + format: date-time + couponIds: + type: + - array + - 'null' + description: >- + List of coupons to redeem on the customer and restrict to this + order. + + + For more information, see + [Coupons](https://www.rebilly.com/docs/settings/coupons-and-discounts/). + + + This parameter uses the following logic: + + + - If this parameter is not supplied, applied coupons are not + changed. + + - If an empty array is supplied, all applied coupon redemptions are + canceled. + + - If a list of coupons is supplied, unapplied coupons in the list + are applied. + Coupons that have already been applied do not change state. + Applied coupons that are not supplied in list are canceled. + + If the list of applied coupons on a pending order is changed by this + parameter during an order update, the invoice for the order is + reissued. + writeOnly: true + items: + type: string + description: ID of the coupon. + poNumber: + description: Purchase order number displayed on the issued invoices. + type: + - string + - 'null' + example: PO123456 + shipping: + $ref: '#/components/schemas/Shipping' + notes: + description: Notes for the customer displayed on the order invoice. + type: string + status: + description: >- + Status of the subscription service. + + A subscription starts in the `pending` status, and becomes `active` + when the service period begins. + type: string + readOnly: true + enum: + - pending + - active + - abandoned + - canceled + - churned + - paused + - voided + - completed + - trial-ended + inTrial: + description: Specifies if the subscription is currently in a trial period. + type: boolean + readOnly: true + trial: + type: object + description: >- + Trial details. + + To use plan defaults do not send the `trial` key, or send a `null` + value. + properties: + enabled: + description: |- + Specifies if there is a trial for this subscription. + Plans without trial prices are free trials. + type: boolean + readOnly: true + trial: + type: object + description: >- + Trial details. + + To use plan defaults do not send the `trial` key, or send a + `null` value. + properties: + enabled: + description: |- + Specifies if there is a trial for this subscription. + Plans without trial prices are free trials. + type: boolean + endTime: + description: >- + Time and date when the trial ends. + + If a trial is enabled on this subscription, a value must be + provided. + type: string + format: date-time + isTrialOnly: + description: |- + Specifies if a subscription ends after a trial period. + If this value is `true`, recurring settings are ignored. + type: boolean + default: false + invoiceTimeShift: + description: >- + Shifts issue time and due time of invoices for this + subscription. + + + This setting overrides plan settings. + + To use plan settings, set this value to `null`. + + + To use multiple plans in one subscription, + + all plans must have the same billing period, + + this property enables the customer to subscribe to different + plans. + example: null + oneOf: + - $ref: '#/components/schemas/InvoiceTimeShift' + - type: 'null' + recurringInterval: + type: + - object + - 'null' + description: >- + Recurring interval to override plan settings. + + To use plan settings, set this value to `null`. + + + To use multiple plans in one subscription, + + all plans must have the same same recurring period length, + + this property enables the customer to subscribe to different + plans. + example: null + properties: + periodAnchorInstruction: + $ref: '#/components/schemas/ServicePeriodAnchorInstruction' + autopay: + description: >- + Specifies if payment attempts are made automatically. + + If autopay is enabled, the payment is retrieved from the + customer on the renewal date using the payment instrument that + is set at `paymentInstrumentId`, + + or the default payment instrument on the subscription. + type: boolean + default: true + startTime: + description: |- + Date and time when the subscription starts. + If this value is `null`, the current time is used. + This value cannot be more than one service period in the past. + type: + - string + - 'null' + x-sortable: true + x-basic: true + example: null + format: date-time + endTime: + description: >- + Time and date when the trial ends. + + If a trial is enabled on this subscription, a value must be + provided. + type: string + format: date-time + isTrialOnly: + description: |- + Specifies if a subscription ends after a trial period. + If this value is `true`, recurring settings are ignored. + type: boolean + default: false + invoiceTimeShift: + description: |- + Shifts issue time and due time of invoices for this subscription. + + This setting overrides plan settings. + To use plan settings, set this value to `null`. + + To use multiple plans in one subscription, + all plans must have the same billing period, + this property enables the customer to subscribe to different plans. + example: null + oneOf: + - $ref: '#/components/schemas/InvoiceTimeShift' + - type: 'null' + recurringInterval: + type: + - object + - 'null' + description: |- + Recurring interval to override plan settings. + To use plan settings, set this value to `null`. + + To use multiple plans in one subscription, + all plans must have the same same recurring period length, + this property enables the customer to subscribe to different plans. + example: null + properties: + periodAnchorInstruction: + $ref: '#/components/schemas/ServicePeriodAnchorInstruction' + autopay: + description: >- + Specifies if payment attempts are made automatically. + + If autopay is enabled, the payment is retrieved from the customer on + the renewal date using the payment instrument that is set at + `paymentInstrumentId`, + + or the default payment instrument on the subscription. + type: boolean + default: true + startTime: + description: |- + Date and time when the subscription starts. + If this value is `null`, the current time is used. + This value cannot be more than one service period in the past. + type: + - string + - 'null' + x-sortable: true + x-basic: true + example: null + format: date-time + endTime: + description: Date and time when the subscription ends. + x-sortable: true + type: string + format: date-time + readOnly: true + renewalTime: + description: Date and time when the subscription renews. + type: string + x-sortable: true + x-basic: true + format: date-time + rebillNumber: + description: Current billing period number. + type: integer + readOnly: true + x-sortable: true + lineItems: + description: >- + Subscription line items which queue until the next renewal (or + interim) invoice is issued for the subscription. + readOnly: true + type: array + deprecated: true + items: + type: object + description: >- + Invoice line item. Use `isInterim` property of upcoming invoice + items instead. + deprecated: true + properties: + type: + description: Type of invoice line item. + type: string + enum: + - debit + - credit + description: + description: Description of the line item. + type: string + maxLength: 1000 + unitPriceAmount: + description: Unit price of the line item. + type: number + format: double + example: 49.95 + unitPriceCurrency: + $ref: '#/components/schemas/CurrencyCode' + quantity: + description: Quantity of the line item. + type: integer + example: 1 + periodStartTime: + description: Date and time when the period begins for this item. + type: string + format: date-time + periodEndTime: + description: Date and time when the period ends for this item. + type: string + format: date-time + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + lineItemSubtotal: + type: object + readOnly: true + description: |- + Subtotal of line items in this subscription (signed value). + If credits exceed debits, this value is a negative number. + properties: + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + type: number + x-type: Money + x-sortable: true + description: Amount of the subtotal. + format: double + example: 49.95 + paymentInstrumentId: + type: + - string + - 'null' + description: >- + ID of the payment instrument to use for autopay. + + If this value is not provided, or if the payment instrument is + inactive, + + the customer's default payment instrument is used. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + canceledTime: + description: Date and time when the subscription order is canceled. + x-label: Cancellation time + x-sortable: true + x-basic: true + type: string + format: date-time + readOnly: true + canceledBy: + description: Specifies who initiated the cancellation. + type: string + readOnly: true + enum: + - merchant + - customer + - rebilly + cancelCategory: + description: Category of the cancellation. + type: string + readOnly: true + enum: + - billing-failure + - did-not-use + - did-not-want + - missing-features + - bugs-or-problems + - do-not-remember + - risk-warning + - contract-expired + - too-expensive + - never-started + - switched-plan + - other + cancelDescription: + description: Description of the cancellation reason in free form. + type: string + readOnly: true + maxLength: 255 + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontOneTimeOrder: + type: object + properties: + id: + type: string + description: ID of the order. + readOnly: true + maxLength: 50 + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + orderType: + description: |- + Specifies the type of order. + An order may be a subscription or a one-time purchase. + type: string + enum: + - subscription-order + - one-time-order + default: subscription-order + billingStatus: + description: >- + Billing status of the most recent invoice. + + This value may help you to determine if you should change the + service status of the service, + + such as suspending the service. + type: string + readOnly: true + enum: + - draft + - unpaid + - past-due + - abandoned + - paid + - voided + - refunded + - disputed + - partially-refunded + - partially-paid + websiteId: + x-sortable: true + x-basic: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + currency: + description: Currency of the order. + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + initialInvoiceId: + type: string + description: ID of the initial invoice. + readOnly: true + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + recentInvoiceId: + type: string + description: |- + ID of the most recently issued invoice. + The invoice might not be `paid` yet. + readOnly: true + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + items: + description: Details of items in the order. + type: array + minItems: 1 + items: + $ref: '#/components/schemas/OrderItem' + deliveryAddress: + description: Delivery address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + billingAddress: + description: Billing address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + activationTime: + description: Date and time when the order is activated. + x-sortable: true + $ref: '#/components/schemas/ServerTimestamp' + voidTime: + description: Date and time when the order is voided. + $ref: '#/components/schemas/ServerTimestamp' + abandonTime: + type: + - string + - 'null' + description: >- + Date and time when the pending order is automatically abandoned. + + If this value is not passed during order creation, + + a [pending order + TTL](https://all-rebilly.redoc.ly/tag/Organizations/operation/PatchOrganization/#!path=settings/billing/pendingOrderTtl&t=request) + setting is used to calculate the value. + format: date-time + couponIds: + type: + - array + - 'null' + description: >- + List of coupons to redeem on the customer and restrict to this + order. + + + For more information, see + [Coupons](https://www.rebilly.com/docs/settings/coupons-and-discounts/). + + + This parameter uses the following logic: + + + - If this parameter is not supplied, applied coupons are not + changed. + + - If an empty array is supplied, all applied coupon redemptions are + canceled. + + - If a list of coupons is supplied, unapplied coupons in the list + are applied. + Coupons that have already been applied do not change state. + Applied coupons that are not supplied in list are canceled. + + If the list of applied coupons on a pending order is changed by this + parameter during an order update, the invoice for the order is + reissued. + writeOnly: true + items: + type: string + description: ID of the coupon. + poNumber: + description: Purchase order number displayed on the issued invoices. + type: + - string + - 'null' + example: PO123456 + shipping: + $ref: '#/components/schemas/Shipping' + notes: + description: Notes for the customer displayed on the order invoice. + type: string + status: + description: Status of the one-time order. + type: string + readOnly: true + enum: + - pending + - abandoned + - completed + - canceled + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontOrder: + type: object + discriminator: + propertyName: orderType + mapping: + subscription-order: '#/components/schemas/StorefrontSubscriptionOrder' + one-time-order: '#/components/schemas/StorefrontOneTimeOrder' + properties: + orderType: + description: |- + Specifies the type of order. + An order may be a subscription or a one-time purchase. + type: string + enum: + - subscription-order + - one-time-order + PaymentInstructionsToken: + type: object + title: Payment Token + required: + - token + properties: + token: + description: ID of the payment token. + type: string + PaymentInstructionsInstrument: + type: object + title: Payment instrument + required: + - paymentInstrumentId + properties: + paymentInstrumentId: + type: string + description: ID of the payment instrument. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + StorefrontTransactionRedirectUrl: + type: + - string + - 'null' + description: >- + URL to redirect the end-user when an offsite transaction is completed. + + If `website.url` is `https://example.com`, then the `redirectUrl` could + be set to any of these: + + + - `https://example.com` + + + - `https://example.com/some/path` + + + - `https://example.com/some/path?and=query` + + + - `https://example.com/some/path?and=query#and-fragment` + + + Defaults to the website URL. + + You may use `{id}` or `{result}` as placeholders in the URL and we + replace them with the transaction id and result accordingly. + format: uri + writeOnly: true + StorefrontPaymentCard: + type: object + description: Payment card information. + title: Payment card + properties: + id: + description: ID of the payment instrument. + readOnly: true + $ref: '#/components/schemas/ResourceId' + method: + description: Method of payment instrument. + type: string + readOnly: true + enum: + - payment-card + status: + type: string + description: >- + Status of the payment instrument. + + An `active` status means that a payment instrument has been used at + least once for an approved transaction. + + To remove an instrument from use, set this value to `deactivated`. + + + For more information, see [Deactivate a payment + instrument](../PostPaymentInstrumentDeactivation). + enum: + - active + - inactive + - expired + - deactivated + - verification-needed + fingerprint: + description: |- + Unique value that is used to identify the payment instrument. + This value is generated from the `bin` and the `last4` values. + This value contains alphanumeric characters. + type: string + readOnly: true + bin: + description: >- + Bank Identification Number (BIN) of the payment card. + + This value is the same as the first 6 digits of the associated + Primary Account Number (PAN). + type: string + format: bin + readOnly: true + last4: + description: Last 4 digits of the associated Primary Account Number (PAN). + type: string + readOnly: true + pan: + description: Primary Account Number (PAN) of the payment card. + type: string + writeOnly: true + expYear: + description: Expiration year of the payment card. + type: integer + expMonth: + description: Expiration month of the payment card. + type: integer + cvv: + description: Card Verification Value (CVV) of the payment card. + type: string + writeOnly: true + brand: + readOnly: true + allOf: + - $ref: '#/components/schemas/PaymentCardBrand' + bankCountry: + description: Bank country of the payment instrument. + type: string + readOnly: true + bankName: + description: Bank name of the payment instrument. + type: string + readOnly: true + billingAddress: + description: Contact's billing address. + $ref: '#/components/schemas/ContactObject' + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + billingPortalUrl: + description: URL of the billing portal where the card can be updated. + type: string + readOnly: true + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + token: + description: |- + New customer JSON Web Token (JWT) that is used for further requests. + This value is null if the customer is already authenticated. + type: + - string + - 'null' + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontBankAccount: + type: object + title: Bank account + properties: + id: + description: ID of the payment instrument. + readOnly: true + $ref: '#/components/schemas/ResourceId' + method: + description: Method of payment instrument. + type: string + readOnly: true + enum: + - ach + bankName: + description: Name of the bank. + type: string + routingNumber: + description: Bank routing number. + type: string + accountNumberType: + description: >- + Type of bank account number. + + A valid value is a Basic Bank Account Number (BBAN) or an + International Bank Account Number (IBAN). + type: string + default: BBAN + enum: + - BBAN + - IBAN + accountType: + description: Type of bank account. + type: string + enum: + - checking + - savings + - other + bic: + description: Bank Identifier Code (BIC). + type: string + billingAddress: + description: Customer's billing address. + $ref: '#/components/schemas/ContactObject' + fingerprint: + description: |- + Unique value which identifies the bank account. + This value contains alphanumeric characters. + Depending on the type of bank account number, + a bank account fingerprint is generated using one of the following: + - BBAN: `last4` and `routingNumber` values. + - IBAN: First 8 characters of the IBAN and the `last4` value. + type: string + readOnly: true + last4: + description: Last 4 digits of the bank account number. + type: string + readOnly: true + status: + description: Status of the bank account. + type: string + readOnly: true + enum: + - active + - deactivated + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + token: + description: |- + New customer JSON Web Token (JWT) that is used for further requests. + This value is null if the customer is already authenticated. + type: + - string + - 'null' + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontPayPalAccount: + type: object + title: PayPal account + properties: + id: + description: ID of the payment instrument. + readOnly: true + $ref: '#/components/schemas/ResourceId' + method: + description: Method of payment instrument. + type: string + enum: + - paypal + billingAddress: + description: Billing address. + $ref: '#/components/schemas/ContactObject' + username: + description: PayPal username. + type: string + readOnly: true + status: + description: PayPal account status. + type: string + readOnly: true + enum: + - inactive + - active + - deactivated + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + token: + description: |- + New customer JSON Web Token (JWT) that is used for further requests. + This value is null if the customer is already authenticated. + type: + - string + - 'null' + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontKhelocardCard: + type: object + title: Khelocard card + properties: + id: + description: ID of the payment instrument. + $ref: '#/components/schemas/ResourceId' + method: + description: Method of payment instrument. + type: string + enum: + - Khelocard + fingerprint: + description: >- + Unique value which identifies the payment instrument. + + This value contains alphanumeric characters. + + This value is generated from the card number, CVV, and expiration + date. + type: string + number: + description: Khelocard card masked number. + type: string + last4: + description: Last 4 digits of the number. + type: string + expYear: + description: Khelocard card expiration year. + type: integer + expMonth: + description: Khelocard card expiration month. + type: integer + billingAddress: + description: Billing address. + $ref: '#/components/schemas/ContactObject' + status: + description: Payment instrument status. + type: string + enum: + - active + - deactivated + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + token: + description: |- + New customer JSON Web Token (JWT) that is used for further requests. + This value is null if the customer is already authenticated. + type: + - string + - 'null' + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontAlternativeInstrument: + type: object + title: Alternative instrument + properties: + id: + description: ID of the payment instrument. + readOnly: true + $ref: '#/components/schemas/ResourceId' + method: + description: Payment method of the payment instrument. + allOf: + - $ref: '#/components/schemas/AlternativePaymentMethods' + - not: + enum: + - payment-card + - paypal + - ach + - echeck + - Khelocard + billingAddress: + description: >- + Billing address of the user that is associated with the payment + instrument. + $ref: '#/components/schemas/ContactObject' + status: + description: Payment instrument status. + type: string + readOnly: true + enum: + - active + - deactivated + useAsBackup: + $ref: '#/components/schemas/UseAsBackup' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + token: + description: |- + New customer JSON Web Token (JWT) that is used for further requests. + This value is null if the customer is already authenticated. + type: + - string + - 'null' + readOnly: true + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontPaymentInstrument: + oneOf: + - $ref: '#/components/schemas/StorefrontPaymentCard' + - $ref: '#/components/schemas/StorefrontBankAccount' + - $ref: '#/components/schemas/StorefrontPayPalAccount' + - $ref: '#/components/schemas/StorefrontKhelocardCard' + - $ref: '#/components/schemas/StorefrontAlternativeInstrument' + StorefrontPayoutRequest: + type: object + properties: + id: + type: string + readOnly: true + description: Unique resource ID. + maxLength: 50 + example: pout_req_0YVDMDE2BMC6KBB5MX76RF6T80 + websiteId: + $ref: '#/components/schemas/WebsiteId' + paymentInstrumentId: + type: string + description: ID of the requested payment instrument to offer for the payout. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + currency: + description: Currency of the payout. + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Amount of the payout. + type: number + format: double + status: + description: Status of the request. + type: string + readOnly: true + enum: + - pending + - partially-fulfilled + - fulfilled + x-enumDescriptions: + pending: Request is awaiting fulfillment. + partially-fulfilled: >- + Request is partially paid out when `availableAmount` is less than + `amount`. + fulfilled: Request is fully paid out when `availableAmount` reaches zero. + createdTime: + description: Date and time when the payout request is created. + $ref: '#/components/schemas/ServerTimestamp' + updatedTime: + description: Date and time when the payout request is updated. + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - paymentInstrument + StorefrontPlan: + type: object + required: + - name + - currency + - productId + - pricing + properties: + id: + type: string + description: ID of the plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + name: + description: |- + Name of the plan. + This name is displayed on invoices and receipts. + type: string + maxLength: 255 + description: + type: string + description: |- + Plain-text description of the plan. + This field accepts plain-text only. + richDescription: + type: string + description: >- + Rich-text description of the plan. + + This field accepts rich text formatting, such as: bold, underline, + italic, and hyperlinks. + productId: + type: string + description: ID of the related product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + productOptions: + type: + - object + - 'null' + description: Name-value pairs that specify the product options. + additionalProperties: + type: string + example: + color: red + size: xxl + currency: + $ref: '#/components/schemas/CurrencyCode' + currencySign: + description: Currency sign. + readOnly: true + type: string + pricing: + $ref: '#/components/schemas/PlanPriceFormula' + setup: + type: + - object + - 'null' + description: Set up information of the plan. + required: + - price + properties: + price: + description: |- + Price of setting up the plan. + If your service charges a set up fee, specify it here. + To charge no set up fee, set this value to `0`. + type: number + format: double + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + isActive: + type: boolean + description: Specifies if the plan is active. + default: true + revision: + type: integer + readOnly: true + description: >- + Number of times the plan is modified. + + Compare this value with materialized subscription item revision + values. + trial: + type: + - object + - 'null' + description: |- + Trial configuration setting. + If you do not want to offer a trial, set this value to `null`. + required: + - price + - period + properties: + price: + description: |- + Price of the trial. + For a free trial, set this value to `0`. + type: number + format: double + period: + type: object + description: Period information. + required: + - unit + - length + properties: + unit: + description: Unit of time. + type: string + enum: + - day + - week + - month + - year + length: + description: Length of time. + type: integer + minimum: 1 + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + product: + type: object + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + - attachments + StorefrontProduct: + type: object + required: + - name + description: >- + Products are the items that your business sells. + + Products include digital goods, services, and physical goods. + + Products appear on invoice line items. + + If you set a tax category identifier, taxes are calculated at invoice + generation. + + If the product is shippable, shipping costs are calculated at invoice + generation. + + Pricing and variations are set within plans. + + For more information, see + [Plans](https://www.rebilly.com/docs/dev-docs/api/tag/Plans/). + properties: + id: + type: string + description: ID of the product. + readOnly: true + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + name: + description: Name of the product. + type: string + maxLength: 255 + example: Premium membership + unitLabel: + description: 'Unit label, such as per `seat` or per `unit`.' + type: string + maxLength: 50 + example: seat + default: unit + description: + description: Description of the product. + type: + - string + - 'null' + maxLength: 512 + requiresShipping: + description: |- + Specifies if the product requires shipping. + If this value is `true`, shipping calculations are applied. + type: boolean + example: false + options: + description: >- + Product options such as color, size, and so forth. + + Product option values are defined in plans. + + For more information, see + [Plans](https://www.rebilly.com/docs/dev-docs/api/tag/Plans/). + type: array + items: + type: string + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + $ref: '#/components/schemas/SelfLink' + StorefrontPurchase: + type: object + required: + - websiteId + - paymentInstruction + - items + properties: + orderId: + description: ID of the order. + readOnly: true + $ref: '#/components/schemas/ResourceId' + invoice: + description: Initial invoice. + readOnly: true + allOf: + - $ref: '#/components/schemas/StorefrontInvoice' + transaction: + description: Initial transaction. + readOnly: true + allOf: + - $ref: '#/components/schemas/StorefrontTransaction' + token: + description: >- + Session token that is used for authentication. + + This token can be used to view the created order, invoice, and + transaction. + type: string + readOnly: true + websiteId: + writeOnly: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + items: + type: array + writeOnly: true + minItems: 1 + items: + type: object + required: + - planId + properties: + planId: + type: string + description: ID of the plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + quantity: + description: Number of product units in the specified plan. + type: integer + billingAddress: + description: Billing address details. + writeOnly: true + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + deliveryAddress: + description: Delivery address details. + writeOnly: true + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + shippingRateId: + description: |- + ID of the shipping rate. If this value is not set, + the cheapest applicable shipping rate is chosen. + writeOnly: true + $ref: '#/components/schemas/ResourceId' + paymentInstruction: + description: Payment instruction for the purchase. + writeOnly: true + allOf: + - $ref: '#/components/schemas/PaymentInstruction' + couponIds: + type: array + description: List of coupons that are applied to the order. + writeOnly: true + items: + type: string + password: + type: + - string + - 'null' + description: |- + Customer account password. + If this value is set, it is used to create a customer account. + writeOnly: true + redirectUrl: + type: + - string + - 'null' + description: >- + URL to redirect the end-user when an offsite transaction is + completed. + + If `website.url` is `https://example.com`, + + then the `redirectUrl` can be set to one the following: + + + - `https://example.com` + + + - `https://example.com/some/path` + + + - `https://example.com/some/path?and=query` + + + - `https://example.com/some/path?and=query#and-fragment` + + + This field defaults to the configured website URL. + + You may use `{id}` or `{result}` as placeholders in the URL. + + These placeholders are replaced with the transaction ID and result. + format: uri + writeOnly: true + StorefrontOrderPreview: + type: object + required: + - websiteId + - items + properties: + websiteId: + writeOnly: true + allOf: + - $ref: '#/components/schemas/WebsiteId' + items: + description: Items details. + type: array + writeOnly: true + minItems: 1 + items: + type: object + required: + - planId + properties: + planId: + type: string + description: ID of the plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + quantity: + description: Number of product units in the specified plan. + type: integer + billingAddress: + description: Billing address details. + writeOnly: true + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + deliveryAddress: + description: Delivery address details. + writeOnly: true + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + couponIds: + type: array + writeOnly: true + description: List of coupons that are applied to the order. + items: + type: string + currency: + readOnly: true + $ref: '#/components/schemas/CurrencyCode' + lineItems: + type: array + description: List of purchase items. + readOnly: true + items: + type: object + properties: + type: + description: Type of transaction. + type: string + enum: + - debit + - credit + description: + description: Description of the purchase item. + type: string + unitPrice: + description: Unit price of the purchase item. + type: number + format: double + example: 49.95 + quantity: + description: Total quantity of the purchase item. + type: number + format: integer + price: + description: Total cost of the purchase item. + type: number + format: double + planId: + type: string + description: ID of the related plan. + maxLength: 50 + example: plan_0YV7DENSVGDBW9S71XZNNYYQ0X + productId: + type: string + description: ID of the related product. + maxLength: 50 + example: prod_0YV7DES3WPC5J8JD8QTVNZBZNZ + shippingRates: + type: array + description: Available shipping rates. + readOnly: true + items: + $ref: '#/components/schemas/ShippingOption' + taxes: + type: array + description: Taxes applied to the purchase. + readOnly: true + items: + type: object + properties: + amount: + description: Amount of tax that is due. + type: number + format: double + description: + type: string + description: Description of the tax. + discounts: + type: array + description: Discounts applied to the purchase. + readOnly: true + items: + type: object + properties: + couponId: + type: string + description: ID of a coupon that is associated with the purchase. + maxLength: 50 + example: cpn_0YVCNKF81GD778N4YNVGDJK558 + amount: + description: Total amount of discount from the coupon. + type: number + format: double + description: + description: Description of the discount. + type: string + subtotalAmount: + description: Subtotal amount of the purchase. + type: number + format: double + readOnly: true + taxAmount: + description: Tax amount of the purchase. + type: number + format: double + readOnly: true + shippingAmount: + description: Shipping amount of the purchase. + type: number + format: double + readOnly: true + discountsAmount: + description: Total discount amount of the purchase. + type: number + format: double + readOnly: true + total: + description: Total amount of the purchase. + type: number + format: double + readOnly: true + shippingRateId: + description: |- + ID of the shipping rate. + If unset the cheapest applicable shipping rate is chosen. + writeOnly: true + $ref: '#/components/schemas/ResourceId' + StorefrontQuote: + type: object + required: + - websiteId + - customerId + - items + properties: + id: + readOnly: true + description: ID of the quote. + type: string + maxLength: 50 + example: qt_0YV7DES3WPC5J8JD8QTVNZBZNZ + type: + description: >- + Specifies the type of quote. A quote can be a subscription or a + one-time purchase. + type: string + readOnly: true + enum: + - subscription-order + - one-time-order + status: + description: Status of quote. + type: string + readOnly: true + enum: + - draft + - issued + - accepted + - rejected + - canceled + - expired + x-enumDescriptions: + draft: Quote can be edited. This quote cannot be sent to a customer. + issued: Quote cannot be edited. This quote can be sent to a customer. + accepted: Customer accepted the quote and created an order. + rejected: Customer rejected the quote. + canceled: Organization canceled the quote. + expired: Expired before customer or organization interaction. + websiteId: + $ref: '#/components/schemas/WebsiteId' + customerId: + $ref: '#/components/schemas/CustomerId' + orderId: + description: ID of the order. + readOnly: true + type: + - string + - 'null' + maxLength: 50 + example: ord_0YV7DES3WPC5J8JD8QTVNZBZNZ + items: + type: array + minItems: 1 + items: + type: object + required: + - plan + - quantity + properties: + id: + description: ID of the quote item. + readOnly: true + type: string + example: qti_0YV7DES3WPC5J8JD8QTVNZBZNZ + quantity: + description: Number of product units in the specified plan. + type: integer + minimum: 1 + plan: + description: Plan details. + oneOf: + - $ref: '#/components/schemas/OriginalPlan' + - $ref: '#/components/schemas/FlexiblePlan' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _embedded: + type: object + description: >- + Embedded objects that are requested by the `expand` query + parameter. + readOnly: true + properties: + product: + type: object + deliveryAddress: + description: Delivery address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + billingAddress: + description: Billing address of the order. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + invoicePreview: + type: object + description: Preview of the quote invoice. + readOnly: true + properties: + currency: + description: Currency of the invoice. + $ref: '#/components/schemas/CurrencyCode' + initialAmounts: + type: object + description: Total amounts of the initial invoice. + properties: + amount: + description: Amount of the invoice. + type: number + x-type: Money + format: double + subtotalAmount: + description: Subtotal amount of the invoice. + type: number + x-type: Money + format: double + discountAmount: + description: Discount amount that is applied to the invoice. + type: number + x-type: Money + format: double + shippingAmount: + description: Shipping amount that is applied to the invoice. + type: number + x-type: Money + format: double + taxAmount: + description: Tax amount that is applied to the invoice. + type: number + x-type: Money + format: double + recurringAmounts: + type: + - object + - 'null' + description: >- + Total amounts of the recurring invoice. + + This includes recurring items only. + + If the quote does not have recurring items, the value of this + field is `null`. + properties: + amount: + description: Amount of the invoice. + type: number + x-type: Money + format: double + subtotalAmount: + description: Subtotal amount of the invoice. + type: number + x-type: Money + format: double + discountAmount: + description: Discount amount that is applied to the invoice. + type: number + x-type: Money + format: double + shippingAmount: + description: Shipping amount that is applied to the invoice. + type: number + x-type: Money + format: double + taxAmount: + description: Tax amount that is applied to the invoice. + type: number + x-type: Money + format: double + items: + type: array + description: Invoice items. + items: + type: object + properties: + quoteItemId: + description: ID of the related quote item. + type: string + example: qti_0YV7DES3WPC5J8JD8QTVNZBZNZ + type: + type: string + description: Type of the invoice item. + x-basic: true + enum: + - debit + - credit + name: + description: Name of the invoice item. + type: string + maxLength: 1000 + description: + description: Description of the invoice item. + type: string + example: Charge per approved transaction with DCC + maxLength: 255 + priceDescription: + type: string + description: Price description of the invoice item. + example: 50% of the markup for approved transactions + maxLength: 255 + unitPrice: + description: Unit price of the invoice item. + type: + - number + - 'null' + format: double + quantity: + description: Quantity of the invoice item. + type: integer + period: + description: Date interval of the invoice item. + type: + - string + - 'null' + setupUnitPrice: + description: Unit price of the invoice item. + type: + - number + - 'null' + format: double + trialUnitPrice: + description: Unit price of the invoice item. + type: + - number + - 'null' + format: double + trialPeriod: + description: Date interval of the invoice item trial. + type: + - string + - 'null' + taxAmount: + description: Invoice item tax. + type: + - number + - 'null' + format: double + setupTaxAmount: + description: Tax amount of the setup that is applied to the invoice. + type: + - number + - 'null' + format: double + trialTaxAmount: + description: Tax amount of the trial that is applied to the invoice. + type: + - number + - 'null' + format: double + paymentTerms: + description: Payment terms for the customer which are displayed on the quote. + type: string + expirationTime: + description: >- + Date and time when the quote expires. The default expiration time is + one month from the time the quote is issued. + + In a `draft` state, this field may be `null`. + type: + - string + - 'null' + format: date-time + issuedTime: + description: Date and time when the quote is issued. + type: + - string + - 'null' + readOnly: true + format: date-time + acceptedTime: + description: Date and time when the quote is accepted. + type: + - string + - 'null' + readOnly: true + format: date-time + rejectedTime: + description: Date and time when the quote is rejected. + type: + - string + - 'null' + readOnly: true + format: date-time + canceledTime: + description: Date and time when the quote is canceled. + type: + - string + - 'null' + readOnly: true + format: date-time + redirectUrl: + description: >- + URL to redirect the customer to when a quote is rejected. The + default value is the website URL. + type: string + format: uri + signature: + type: object + properties: + showWrittenSignatureLines: + type: boolean + default: false + description: Specifies whether to show written signature lines. + organizationPrintedName: + type: + - string + - 'null' + description: Printed name of the organization. + default: null + shipping: + $ref: '#/components/schemas/Shipping' + tax: + $ref: '#/components/schemas/Taxes' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: >- + Type of link. If quote is accepted, a customer is redirected + to `invoicePaymentFormUrl` + + to pay the initial invoice using one of the methods which are + available to the customer. + type: string + enum: + - self + - invoicePaymentFormUrl + _embedded: + type: object + description: Embedded objects that are requested by the `expand` query parameter. + readOnly: true + properties: + customer: + type: object + website: + type: object + ReadyToPayout: + type: object + required: + - websiteId + - amount + - currency + - riskMetadata + properties: + websiteId: + $ref: '#/components/schemas/WebsiteId' + billingAddress: + description: Billing address. + writeOnly: true + allOf: + - $ref: '#/components/schemas/ContactObject' + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Amount to payout. + type: number + format: double + ReadyToPayoutMethodFilters: + type: array + description: >- + For the method to be applicable, one or more of the following filters + must match. + + If no filters are sent, no restrictions are applied. + + For more information, see [Using + filters](https://all-rebilly.redoc.ly/#section/Using-filter-with-collections). + items: + type: string + ReadyToPayoutPaymentCardMethod: + type: object + title: Payment card + required: + - method + properties: + method: + type: string + description: Payment method. + enum: + - payment-card + brands: + type: array + description: List of supported brands. + minItems: 1 + items: + $ref: '#/components/schemas/PaymentCardBrand' + filters: + $ref: '#/components/schemas/ReadyToPayoutMethodFilters' + ReadyToPayoutGenericMethod: + type: object + title: Generic + required: + - method + properties: + method: + $ref: '#/components/schemas/AlternativePaymentMethods' + filters: + $ref: '#/components/schemas/ReadyToPayoutMethodFilters' + ReadyToPayoutMethods: + type: object + discriminator: + propertyName: method + mapping: + payment-card: '#/components/schemas/ReadyToPayoutPaymentCardMethod' + ach: '#/components/schemas/ReadyToPayoutGenericMethod' + cash: '#/components/schemas/ReadyToPayoutGenericMethod' + check: '#/components/schemas/ReadyToPayoutGenericMethod' + paypal: '#/components/schemas/ReadyToPayoutGenericMethod' + AdvCash: '#/components/schemas/ReadyToPayoutGenericMethod' + Alfa-click: '#/components/schemas/ReadyToPayoutGenericMethod' + Alipay: '#/components/schemas/ReadyToPayoutGenericMethod' + AstroPay Card: '#/components/schemas/ReadyToPayoutGenericMethod' + AstroPay-GO: '#/components/schemas/ReadyToPayoutGenericMethod' + BankReferenced: '#/components/schemas/ReadyToPayoutGenericMethod' + bank-transfer: '#/components/schemas/ReadyToPayoutGenericMethod' + bank-transfer-2: '#/components/schemas/ReadyToPayoutGenericMethod' + bank-transfer-3: '#/components/schemas/ReadyToPayoutGenericMethod' + bank-transfer-4: '#/components/schemas/ReadyToPayoutGenericMethod' + bank-transfer-5: '#/components/schemas/ReadyToPayoutGenericMethod' + bank-transfer-6: '#/components/schemas/ReadyToPayoutGenericMethod' + bank-transfer-7: '#/components/schemas/ReadyToPayoutGenericMethod' + bank-transfer-8: '#/components/schemas/ReadyToPayoutGenericMethod' + bank-transfer-9: '#/components/schemas/ReadyToPayoutGenericMethod' + Baloto: '#/components/schemas/ReadyToPayoutGenericMethod' + Beeline: '#/components/schemas/ReadyToPayoutGenericMethod' + Belfius-direct-net: '#/components/schemas/ReadyToPayoutGenericMethod' + bitcoin: '#/components/schemas/ReadyToPayoutGenericMethod' + Bizum: '#/components/schemas/ReadyToPayoutGenericMethod' + Boleto: '#/components/schemas/ReadyToPayoutGenericMethod' + cash-deposit: '#/components/schemas/ReadyToPayoutGenericMethod' + CASHlib: '#/components/schemas/ReadyToPayoutGenericMethod' + CashToCode: '#/components/schemas/ReadyToPayoutGenericMethod' + China UnionPay: '#/components/schemas/ReadyToPayoutGenericMethod' + Cleo: '#/components/schemas/ReadyToPayoutGenericMethod' + CODVoucher: '#/components/schemas/ReadyToPayoutGenericMethod' + Conekta-oxxo: '#/components/schemas/ReadyToPayoutGenericMethod' + Cupon-de-pagos: '#/components/schemas/ReadyToPayoutGenericMethod' + cryptocurrency: '#/components/schemas/ReadyToPayoutGenericMethod' + domestic-cards: '#/components/schemas/ReadyToPayoutGenericMethod' + echeck: '#/components/schemas/ReadyToPayoutGenericMethod' + ecoPayz: '#/components/schemas/ReadyToPayoutGenericMethod' + ecoVoucher: '#/components/schemas/ReadyToPayoutGenericMethod' + Efecty: '#/components/schemas/ReadyToPayoutGenericMethod' + EPS: '#/components/schemas/ReadyToPayoutGenericMethod' + ePay.bg: '#/components/schemas/ReadyToPayoutGenericMethod' + eZeeWallet: '#/components/schemas/ReadyToPayoutGenericMethod' + FasterPay: '#/components/schemas/ReadyToPayoutGenericMethod' + Flexepin: '#/components/schemas/ReadyToPayoutGenericMethod' + Giropay: '#/components/schemas/ReadyToPayoutGenericMethod' + Gpaysafe: '#/components/schemas/ReadyToPayoutGenericMethod' + Google Pay: '#/components/schemas/ReadyToPayoutGenericMethod' + iDebit: '#/components/schemas/ReadyToPayoutGenericMethod' + iDEAL: '#/components/schemas/ReadyToPayoutGenericMethod' + ING-homepay: '#/components/schemas/ReadyToPayoutGenericMethod' + INOVAPAY-pin: '#/components/schemas/ReadyToPayoutGenericMethod' + INOVAPAY-wallet: '#/components/schemas/ReadyToPayoutGenericMethod' + InstaDebit: '#/components/schemas/ReadyToPayoutGenericMethod' + instant-bank-transfer: '#/components/schemas/ReadyToPayoutGenericMethod' + InstantPayments: '#/components/schemas/ReadyToPayoutGenericMethod' + Interac: '#/components/schemas/ReadyToPayoutGenericMethod' + Interac-online: '#/components/schemas/ReadyToPayoutGenericMethod' + Interac-eTransfer: '#/components/schemas/ReadyToPayoutGenericMethod' + invoice: '#/components/schemas/ReadyToPayoutGenericMethod' + iWallet: '#/components/schemas/ReadyToPayoutGenericMethod' + Jeton: '#/components/schemas/ReadyToPayoutGenericMethod' + jpay: '#/components/schemas/ReadyToPayoutGenericMethod' + Khelocard: '#/components/schemas/ReadyToPayoutGenericMethod' + Klarna: '#/components/schemas/ReadyToPayoutGenericMethod' + KNOT: '#/components/schemas/ReadyToPayoutGenericMethod' + loonie: '#/components/schemas/ReadyToPayoutGenericMethod' + Matrix: '#/components/schemas/ReadyToPayoutGenericMethod' + MaxiCash: '#/components/schemas/ReadyToPayoutGenericMethod' + Megafon: '#/components/schemas/ReadyToPayoutGenericMethod' + MiFinity-eWallet: '#/components/schemas/ReadyToPayoutGenericMethod' + miscellaneous: '#/components/schemas/ReadyToPayoutGenericMethod' + Bancontact: '#/components/schemas/ReadyToPayoutGenericMethod' + Bancontact-mobile: '#/components/schemas/ReadyToPayoutGenericMethod' + MTS: '#/components/schemas/ReadyToPayoutGenericMethod' + MuchBetter: '#/components/schemas/ReadyToPayoutGenericMethod' + Multibanco: '#/components/schemas/ReadyToPayoutGenericMethod' + Neosurf: '#/components/schemas/ReadyToPayoutGenericMethod' + Netbanking: '#/components/schemas/ReadyToPayoutGenericMethod' + Neteller: '#/components/schemas/ReadyToPayoutGenericMethod' + Nordea-Solo: '#/components/schemas/ReadyToPayoutGenericMethod' + OchaPay: '#/components/schemas/ReadyToPayoutGenericMethod' + online-bank-transfer: '#/components/schemas/ReadyToPayoutGenericMethod' + Onlineueberweisen: '#/components/schemas/ReadyToPayoutGenericMethod' + oriental-wallet: '#/components/schemas/ReadyToPayoutGenericMethod' + OXXO: '#/components/schemas/ReadyToPayoutGenericMethod' + P24: '#/components/schemas/ReadyToPayoutGenericMethod' + Pagadito: '#/components/schemas/ReadyToPayoutGenericMethod' + PagoEffectivo: '#/components/schemas/ReadyToPayoutGenericMethod' + Pagsmile-deposit-express: '#/components/schemas/ReadyToPayoutGenericMethod' + Pagsmile-lottery: '#/components/schemas/ReadyToPayoutGenericMethod' + PayCash: '#/components/schemas/ReadyToPayoutGenericMethod' + Payeer: '#/components/schemas/ReadyToPayoutGenericMethod' + PaymentAsia-crypto: '#/components/schemas/ReadyToPayoutGenericMethod' + Paymero: '#/components/schemas/ReadyToPayoutGenericMethod' + Perfect-money: '#/components/schemas/ReadyToPayoutGenericMethod' + Piastrix: '#/components/schemas/ReadyToPayoutGenericMethod' + plaid-account: '#/components/schemas/ReadyToPayoutGenericMethod' + PayTabs: '#/components/schemas/ReadyToPayoutGenericMethod' + Paysafecard: '#/components/schemas/ReadyToPayoutGenericMethod' + Paysafecash: '#/components/schemas/ReadyToPayoutGenericMethod' + Pay4Fun: '#/components/schemas/ReadyToPayoutGenericMethod' + Paynote: '#/components/schemas/ReadyToPayoutGenericMethod' + PinPay: '#/components/schemas/ReadyToPayoutGenericMethod' + PIX: '#/components/schemas/ReadyToPayoutGenericMethod' + phone: '#/components/schemas/ReadyToPayoutGenericMethod' + PhonePe: '#/components/schemas/ReadyToPayoutGenericMethod' + POLi: '#/components/schemas/ReadyToPayoutGenericMethod' + PostFinance-card: '#/components/schemas/ReadyToPayoutGenericMethod' + PostFinance-e-finance: '#/components/schemas/ReadyToPayoutGenericMethod' + QIWI: '#/components/schemas/ReadyToPayoutGenericMethod' + QPay: '#/components/schemas/ReadyToPayoutGenericMethod' + QQPay: '#/components/schemas/ReadyToPayoutGenericMethod' + rapyd-checkout: '#/components/schemas/ReadyToPayoutGenericMethod' + Resurs: '#/components/schemas/ReadyToPayoutGenericMethod' + SafetyPay: '#/components/schemas/ReadyToPayoutGenericMethod' + SEPA: '#/components/schemas/ReadyToPayoutGenericMethod' + Skrill: '#/components/schemas/ReadyToPayoutGenericMethod' + Skrill Rapid Transfer: '#/components/schemas/ReadyToPayoutGenericMethod' + SMSVoucher: '#/components/schemas/ReadyToPayoutGenericMethod' + Sofort: '#/components/schemas/ReadyToPayoutGenericMethod' + SparkPay: '#/components/schemas/ReadyToPayoutGenericMethod' + swift-dbt: '#/components/schemas/ReadyToPayoutGenericMethod' + Tele2: '#/components/schemas/ReadyToPayoutGenericMethod' + Terminaly-RF: '#/components/schemas/ReadyToPayoutGenericMethod' + ToditoCash-card: '#/components/schemas/ReadyToPayoutGenericMethod' + Trustly: '#/components/schemas/ReadyToPayoutGenericMethod' + UPayCard: '#/components/schemas/ReadyToPayoutGenericMethod' + UPI: '#/components/schemas/ReadyToPayoutGenericMethod' + USD-coin: '#/components/schemas/ReadyToPayoutGenericMethod' + VCreditos: '#/components/schemas/ReadyToPayoutGenericMethod' + VenusPoint: '#/components/schemas/ReadyToPayoutGenericMethod' + voucher: '#/components/schemas/ReadyToPayoutGenericMethod' + voucher-2: '#/components/schemas/ReadyToPayoutGenericMethod' + voucher-3: '#/components/schemas/ReadyToPayoutGenericMethod' + voucher-4: '#/components/schemas/ReadyToPayoutGenericMethod' + Webmoney: '#/components/schemas/ReadyToPayoutGenericMethod' + Webpay: '#/components/schemas/ReadyToPayoutGenericMethod' + Webpay-2: '#/components/schemas/ReadyToPayoutGenericMethod' + Webpay Card: '#/components/schemas/ReadyToPayoutGenericMethod' + WeChat Pay: '#/components/schemas/ReadyToPayoutGenericMethod' + wire: '#/components/schemas/ReadyToPayoutGenericMethod' + XPay-P2P: '#/components/schemas/ReadyToPayoutGenericMethod' + XPay-QR: '#/components/schemas/ReadyToPayoutGenericMethod' + Yandex-money: '#/components/schemas/ReadyToPayoutGenericMethod' + Zotapay: '#/components/schemas/ReadyToPayoutGenericMethod' + Zimpler: '#/components/schemas/ReadyToPayoutGenericMethod' + anyOf: + - $ref: '#/components/schemas/ReadyToPayoutPaymentCardMethod' + - $ref: '#/components/schemas/ReadyToPayoutGenericMethod' + EddScore: + type: string + enum: + - not-found + - unlikely + - unclear + - probable + - confirmed + EddScoreDetails: + type: object + properties: + url: + type: string + format: uri + description: URL from which the score is obtained. + details: + type: string + description: Text from which the score is obtained. + Edd: + type: object + properties: + parsedScore: + type: object + properties: + occupation: + $ref: '#/components/schemas/EddScore' + arrest: + $ref: '#/components/schemas/EddScore' + bankruptcy: + $ref: '#/components/schemas/EddScore' + fraud: + $ref: '#/components/schemas/EddScore' + occupationDetails: + $ref: '#/components/schemas/EddScoreDetails' + arrestDetails: + $ref: '#/components/schemas/EddScoreDetails' + bankruptcyDetails: + $ref: '#/components/schemas/EddScoreDetails' + fraudDetails: + $ref: '#/components/schemas/EddScoreDetails' + score: + type: object + properties: + occupation: + $ref: '#/components/schemas/EddScore' + arrest: + $ref: '#/components/schemas/EddScore' + bankruptcy: + $ref: '#/components/schemas/EddScore' + fraud: + $ref: '#/components/schemas/EddScore' + nextUpdateTime: + description: Date and time in ISO 8601 format when the EDD score is updated. + type: + - string + - 'null' + format: date-time + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + EddTimeline: + type: object + properties: + id: + type: string + description: ID of the timeline message. + readOnly: true + maxLength: 50 + example: tmln_0YV8Q9WEF5DTA8HFXS27D1G6GC + type: + description: Type of timeline message. + type: string + readOnly: true + enum: + - edd-search-performed + - edd-score-manually-updated + - timeline-comment-created + triggeredBy: + description: 'Specifies who, or what, triggered the timeline event.' + type: string + readOnly: true + enum: + - rebilly + - app + - direct-api + message: + description: Detailed message description. + type: string + extraData: + $ref: '#/components/schemas/TimelineExtraData' + occurredTime: + description: Date and time when the customer timeline custom event occurs. + readOnly: true + $ref: '#/components/schemas/ServerTimestamp' + _links: + $ref: '#/components/schemas/SelfLink' + EddSearchResult: + type: object + properties: + id: + type: string + readOnly: true + description: ID of the EDD search result. + maxLength: 50 + example: cus_edd_01H057XGZ62JZED3P8DWAXPAE6 + type: + type: string + readOnly: true + enum: + - occupation + - arrest + - fraud + - bankruptcy + searchResultData: + type: array + readOnly: true + items: + $ref: '#/components/schemas/EddScoreDetails' + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + RiskScoreBoolean: + type: + - object + - 'null' + required: + - value + properties: + value: + type: integer + description: Value added to the risk score of the transaction. + RiskScoreBracket: + type: + - object + - 'null' + required: + - brackets + properties: + brackets: + description: >- + Risk factor values range with corresponding risk score increment + value. First matched bracket is applied. + type: array + minItems: 1 + items: + type: object + required: + - start + - end + - value + properties: + start: + description: Minimal risk factor value when condition is applied. + type: integer + minimum: 0 + end: + description: Maximal risk factor value when condition is applied. + type: integer + minimum: 1 + value: + type: integer + description: Value added to the risk score of the transaction. + minimum: 1 + RiskScoreRules: + type: object + description: Risk score rules. + required: + - isProxy + - isVpn + - isTor + - isHosting + - hasMismatchedBillingAddressCountry + - hasMismatchedBankCountry + - hasMismatchedTimeZone + - hasMismatchedHolderName + - hasFakeName + - isHighRiskCountry + - paymentInstrumentVelocity + - declinedPaymentInstrumentVelocity + - deviceVelocity + - ipVelocity + - emailVelocity + - billingAddressVelocity + properties: + isProxy: + description: Specifies whether the customer's IP address is related to a proxy. + $ref: '#/components/schemas/RiskScoreBoolean' + isVpn: + description: Specifies whether the customer's IP address is related to a VPN. + $ref: '#/components/schemas/RiskScoreBoolean' + isTor: + description: Specifies whether the customer's IP address is related to TOR. + $ref: '#/components/schemas/RiskScoreBoolean' + isHosting: + description: Specifies whether the customer's IP address is related to hosting. + $ref: '#/components/schemas/RiskScoreBoolean' + hasMismatchedBillingAddressCountry: + description: >- + Specifies whether the customer's billing address country and geo-IP + address are not the same. + $ref: '#/components/schemas/RiskScoreBoolean' + hasMismatchedBankCountry: + description: >- + Specifies whether the customer's bank country and geo-IP address are + not the same. + $ref: '#/components/schemas/RiskScoreBoolean' + hasMismatchedTimeZone: + description: >- + Specifies whether the customer's browser time zone and the IP + address associated time zone are not the same. + $ref: '#/components/schemas/RiskScoreBoolean' + hasMismatchedHolderName: + description: >- + Specifies whether the customer's billing address name and primary + address name are not the same. + $ref: '#/components/schemas/RiskScoreBoolean' + hasFakeName: + description: Specifies whether the holder name seems fake. + $ref: '#/components/schemas/RiskScoreBoolean' + isHighRiskCountry: + description: >- + Specifies whether the geo-IP country, or the customer's billing + country, is considered a high risk country. + $ref: '#/components/schemas/RiskScoreBoolean' + paymentInstrumentVelocity: + description: >- + Number of transactions for this payment instrument, based on + fingerprint, in the last 24 hours. + $ref: '#/components/schemas/RiskScoreBracket' + declinedPaymentInstrumentVelocity: + description: >- + Number of declined transactions for this payment instrument + fingerprint in the last 24 hours. + $ref: '#/components/schemas/RiskScoreBracket' + deviceVelocity: + description: >- + Number of transactions for this device, based on fingerprint, in the + last 24 hours. + $ref: '#/components/schemas/RiskScoreBracket' + ipVelocity: + description: Number of transactions for this IP address in the last 24 hours. + $ref: '#/components/schemas/RiskScoreBracket' + emailVelocity: + description: Number of transactions for this email address in the last 24 hours. + $ref: '#/components/schemas/RiskScoreBracket' + billingAddressVelocity: + description: >- + Number of transactions for this billing address in the last 24 + hours. + $ref: '#/components/schemas/RiskScoreBracket' + RiskScoreBlocklistType: + type: + - object + - 'null' + required: + - riskScoreThreshold + - ttl + properties: + riskScoreThreshold: + type: integer + description: Pass and fail threshold for the blocklist. + ttl: + type: integer + description: |- + Amount of seconds a blocklist exists before it expires. + If value for this field is `0` then blocklist record never expires. + RiskScoreBlocklist: + type: object + description: Risk score blocklist configuration. + properties: + address: + description: House number and ZIP code of the customer's address. + $ref: '#/components/schemas/RiskScoreBlocklistType' + bank-account: + description: Bank account being charged. + $ref: '#/components/schemas/RiskScoreBlocklistType' + bin: + description: Bank Identification Number (BIN) of the payment card being charged. + $ref: '#/components/schemas/RiskScoreBlocklistType' + country: + description: >- + Country where the customer is making the purchase, and of the + payment card billing address. + $ref: '#/components/schemas/RiskScoreBlocklistType' + customer-id: + description: Rebilly `customerId` of the customer making the purchase. + $ref: '#/components/schemas/RiskScoreBlocklistType' + email: + description: Email address of the customer making the purchase. + $ref: '#/components/schemas/RiskScoreBlocklistType' + email-domain: + description: Email address domain of the customer making the purchase. + $ref: '#/components/schemas/RiskScoreBlocklistType' + fingerprint: + description: >- + Fingerprint of the user's device. + + A device fingerprint is a unique token that is used to identify the + user. + + The device fingerprint is generated based on device attributes, + + such as: hardware, software, IP address, language, browser, and + more. + $ref: '#/components/schemas/RiskScoreBlocklistType' + ip-address: + description: IP address of the customer making the purchase. + $ref: '#/components/schemas/RiskScoreBlocklistType' + payment-card: + description: Payment card number being charged. + $ref: '#/components/schemas/RiskScoreBlocklistType' + Allowlist: + type: object + description: Risk score allowlist record. + required: + - type + - value + properties: + id: + type: string + description: ID of the allowlist record. + readOnly: true + maxLength: 50 + example: rsal_0YVF9605RKC62BP14NE2R7V2XT + type: + type: string + description: >- + Type of data to exclude from risk score checks. + + Data types that are added to this field are excluded from risk score + checks and are not added to blocklists. + enum: + - address + - bank-account + - bin + - country + - customer-id + - email + - email-domain + - fingerprint + - ip-address + - payment-card + value: + type: string + description: Value of the allowlist record. + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + TagUntagRule: + type: object + description: Tag or untag rule. + required: + - name + - eventType + - addTags + - removeTags + properties: + id: + type: string + description: ID of the rule. + readOnly: true + maxLength: 50 + example: tag_rule_0YVCEE5APAD7V84MK3P9CCJVYY + name: + description: Name of the rule. + type: string + eventType: + allOf: + - $ref: '#/components/schemas/GlobalEventType' + - not: + enum: + - application-instance-disabled + - application-instance-enabled + - balance-transaction-settled + - credit-memo-applied + - credit-memo-created + - credit-memo-modified + - credit-memo-partially-applied + - credit-memo-voided + - customer-redirected-offsite + - customer-returned + - dispute-created + - dispute-modified + - gateway-account-downtime-ended + - gateway-account-downtime-started + - gateway-account-limit-reached + - gateway-account-onboarding-completed + - gateway-account-onboarding-failed + - gateway-account-requested + - invoice-created + - invoice-modified + - invoice-past-due-reminder + - invoice-tax-calculation-failed + - nsf-response-received + - offsite-payment-completed + - order-abandon-reminder + - organization-tax-number-validated + - payment-card-created + - payment-card-expiration-reminder + - payment-instrument-modified + - renewal-invoice-payment-canceled + - risk-score-changed + - subscription-modified + - subscription-pause-created + - subscription-pause-modified + - subscription-pause-revoked + - subscription-renewal-reminder + - subscription-trial-converted + - subscription-trial-end-changed + - subscription-trial-end-reminder + - subscription-trial-ended + - transaction-process-requested + - transaction-processed + - transaction-reconciled + - transaction-timeout-resolved + - waiting-gateway-transaction-completed + - data-export-completed + - hard-usage-limit-reached.yaml + - soft-usage-limit-reached.yaml + filter: + description: >- + Filter that determines whether to tag or untag. + + This field requires a special format. + + Use `,` for multiple allowed values. + + Use `;` for multiple fields. + + + For more information, + + see [Using + filters](https://api-reference.rebilly.com/#section/Using-filter-with-collections). + type: + - string + - 'null' + addTags: + description: List of tags to add to the customer. + type: array + minItems: 0 + maxItems: 1000 + items: + type: string + removeTags: + description: List of tags to remove for the customer. + type: array + minItems: 0 + maxItems: 1000 + items: + type: string + status: + description: Status of the rule. + type: string + default: active + enum: + - active + - inactive + createdTime: + $ref: '#/components/schemas/CreatedTime' + updatedTime: + $ref: '#/components/schemas/UpdatedTime' + _links: + type: array + description: Related resource links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - self + responses: + Unauthorized: + description: Unauthorized access. Invalid credentials used. + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + Forbidden: + description: Access forbidden. + content: + application/json: + schema: + $ref: '#/components/schemas/Forbidden' + NotFound: + description: Resource not found. + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + ValidationError: + description: Invalid data sent. + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + Conflict: + description: Conflict. + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + Customer: + description: Customer. + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' + Found: + description: Resource moved. + headers: + Location: + schema: + type: string + format: uri + example: 'https://api.rebilly.com/example' + NoContent: + description: Request accepted. No response body returned. + requestBodies: + Attachment: + content: + application/json: + schema: + $ref: '#/components/schemas/Attachment' + description: Attachment resource. + required: true + Blocklist: + content: + application/json: + schema: + $ref: '#/components/schemas/Blocklist' + description: Blocklist resource. + required: true + Coupon: + content: + application/json: + schema: + $ref: '#/components/schemas/Coupon' + description: Coupon resource. + required: true + Customer: + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' + description: Customer resource. + required: true + Dispute: + content: + application/json: + schema: + type: object + required: + - currency + - amount + - transactionId + - postedTime + - type + - status + - reasonCode + properties: + transactionId: + description: ID of the disputed transaction. + $ref: '#/components/schemas/TransactionId' + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Dispute amount. + type: number + format: double + acquirerReferenceNumber: + description: Acquirer reference number for the dispute. + type: + - string + - 'null' + caseId: + description: Case ID of the dispute. + type: + - string + - 'null' + reasonCode: + description: >- + Code used in the chargeback that describes the reason for the + dispute. + type: string + enum: + - '0' + - '00' + - '1' + - '2' + - '3' + - '4' + - '5' + - '6' + - '7' + - '8' + - '9' + - '10.1' + - '10.2' + - '10.3' + - '10.4' + - '10.5' + - '11.1' + - '11.2' + - '11.3' + - '12' + - '12.1' + - '12.2' + - '12.3' + - '12.4' + - '12.5' + - '12.6' + - '12.7' + - '13.1' + - '13.2' + - '13.3' + - '13.4' + - '13.5' + - '13.6' + - '13.7' + - '13.8' + - '13.9' + - '30' + - '31' + - '34' + - '35' + - '37' + - '40' + - '41' + - '42' + - '46' + - '47' + - '49' + - '50' + - '51' + - '53' + - '54' + - '55' + - '57' + - '59' + - '60' + - '62' + - '63' + - '70' + - '71' + - '72' + - '73' + - '74' + - '75' + - '76' + - '77' + - '78' + - '79' + - '80' + - '81' + - '82' + - '83' + - '85' + - '86' + - '93' + - '98' + - '1000' + - '2700' + - '2702' + - A + - A01 + - A02 + - A08 + - AL + - AP + - AW + - B + - C02 + - C04 + - C05 + - C08 + - C14 + - C18 + - C28 + - C31 + - C32 + - CA + - CD + - CR + - DA + - DP + - DP1 + - EX + - F10 + - F14 + - F22 + - F24 + - F29 + - FR1 + - FR4 + - FR6 + - IC + - IN + - IS + - LP + - M01 + - M10 + - M49 + - 'N' + - NA + - NC + - P + - P01 + - P03 + - P04 + - P05 + - P07 + - P08 + - P22 + - P23 + - R03 + - R13 + - RG + - RM + - RN1 + - RN2 + - SV + - TF + - TNM + - UA01 + - UA02 + - UA03 + - UA10 + - UA11 + - UA12 + - UA18 + - UA20 + - UA21 + - UA22 + - UA23 + - UA28 + - UA30 + - UA31 + - UA32 + - UA38 + - UA99 + - bank_cannot_process + - credit_not_processed + - customer_initiated + - debit_not_authorized + - duplicate + - fraudulent + - general + - incorrect_account_details + - insufficient_funds + - pre-chargeback-alert + - product_not_received + - product_unacceptable + - subscription_canceled + - unrecognized + type: + description: Type of dispute. + type: string + enum: + - information-request + - first-chargeback + - second-chargeback + - arbitration + - fraud + - ethoca-alert + - verifi-alert + - bank-return + status: + description: Status of the dispute. + type: string + enum: + - response-needed + - under-review + - forfeited + - won + - lost + - unknown + postedTime: + description: Date and time when the dispute is posted. + type: string + format: date-time + deadlineTime: + description: >- + Latest date and time by when a merchant must submit a + representment for a dispute. + + If the deadline is missed, the merchant loses the dispute. + type: + - string + - 'null' + format: date-time + description: Dispute resource. + required: true + Invoice: + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + description: Invoice resource. + required: true + PatchKycRequest: + content: + application/json: + schema: + allOf: + - properties: + customerId: + readOnly: true + - $ref: '#/components/schemas/KycRequest' + PostPaymentInstrument: + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/PaymentInstrumentCreateToken' + - $ref: '#/components/schemas/PaymentCardCreatePlain' + - $ref: '#/components/schemas/BankAccountCreatePlain' + - $ref: '#/components/schemas/PayPalAccount' + - $ref: '#/components/schemas/AlternativeInstrument' + description: PaymentInstrument resource. + required: true + PatchPaymentInstrument: + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/PaymentInstrumentUpdateToken' + - $ref: '#/components/schemas/PaymentCardUpdatePlain' + - $ref: '#/components/schemas/BankAccountUpdatePlain' + description: PaymentInstrument resource. + required: true + Product: + content: + application/json: + schema: + $ref: '#/components/schemas/Product' + description: Product resource. + required: true + Subscription: + content: + application/json: + schema: + $ref: '#/components/schemas/Subscription' + description: Order resource. + required: true + SubscriptionCancellation: + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionCancellation' + description: Cancellation resource. + required: true + Tag: + content: + application/json: + schema: + $ref: '#/components/schemas/Tag' + description: Tag resource. + required: true + UpdateTag: + content: + application/json: + schema: + required: + - name + properties: + name: + description: |- + Unique name for the tag. + Tag names are not case-sensitive. + type: string + maxLength: 255 + pattern: '^[@~\-\.\w\s]+$' + example: New + description: Tag resource. + required: true + TransactionRequest: + content: + application/json: + schema: + required: + - type + - websiteId + - customerId + - currency + - amount + type: object + properties: + upsertCustomer: + type: boolean + default: false + writeOnly: true + description: >- + Specifies whether to create or update (upsert) a customer. + + If this value is `true`, the operation creates or updates + (upserts) a customer. + + If this value is `false`, the `customerId` already exists, and + the related customer is not updated. + type: + description: >- + Type of transaction. + + + This field supports a limited subset of transaction types. + + To refund or void, see [Refund a + transaction](../PostTransactionRefund). + + + To `capture`, use the `sale` type. + + If any existing `authorize` transactions are eligible, + + they are captured and the `sale` converts to a `capture` type. + + + The `setup` type sets up the payment instrument by following + the `setupInstruction` in the selected gateway account. + + If the instruction is to `do-nothing`, a transaction with + result `approved` of type `setup` returns. + type: string + enum: + - sale + - authorize + - setup + limits: + $ref: '#/components/schemas/TransactionLimitAmount' + websiteId: + $ref: '#/components/schemas/WebsiteId' + customerId: + $ref: '#/components/schemas/CustomerId' + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Amount of the transaction. + type: number + format: double + example: 97.97 + invoiceIds: + description: Array of invoice IDs. + type: + - array + - 'null' + items: + $ref: '#/components/schemas/ResourceId' + paymentInstruction: + description: |- + Payment instruction for the purchase. + If this value is not supplied, + the customer's default payment instrument is used. + $ref: '#/components/schemas/PaymentInstruction' + billingAddress: + description: >- + Billing address. + + If this value is not supplied, + + the billing address associated with the payment instrument is + used. + + If no billing address is associated with the payment + instrument, + + the customer's billing address is used. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + requestId: + description: >- + Use this field to prevent duplicate transaction requests that + may occur within a short period of time. + + If a duplicate request is sent with the same `requestId`, + + it is ignored to prevent double-billing. + + This value must be unique within a 24-hour period. + + + > **Important:** This field is recommended. + type: + - string + - 'null' + maxLength: 50 + pattern: '^[\-\w]+$' + example: 44433322-2c4y-483z-a0a9-158621f77a21 + gatewayAccountId: + description: >- + ID of the gateway account. + + Rebilly selects the payment gateway account for the + transaction based on transaction properties and the rules + configuration of the `gateway-account-requested` event. + + To prevent Rebilly from making the gateway account selection, + + supply a gateway account ID in this field. + + Only use this field if you intend to override the settings. + type: + - string + - 'null' + maxLength: 50 + example: gw_acc_0YVCXMF26DDNKAERE5NW727S34 + description: + type: + - string + - 'null' + description: Payment description. + maxLength: 255 + notificationUrl: + description: >- + URL where a server-to-server `POST` notification is sent. + + This notification is sent when the transaction result is + finalized after a timeout or an offsite interaction. + + + Do not interpret this notification as a confirmation, + + complete a `GET` request to confirm the result of the + transaction. + + To ensure the request is not reattempted, + + when the result is confirmed, respond with a `2xx` HTTP status + code. + + + The following placeholders are available to use in this URI: + `{id}` and `{result}`. + + These placeholders are replaced the with the transaction ID + and result accordingly. + type: + - string + - 'null' + format: uri + redirectUrl: + type: + - string + - 'null' + description: >- + URL to redirect the end-user when an offsite transaction is + completed. + + Defaults to the configured URL of the website. + + You may use `{id}` or `{result}` as placeholders in the URL, + + these are replaced the with the transaction ID and result + accordingly. + format: uri + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + isProcessedOutside: + description: Specifies when the transaction is processed outside Rebilly. + type: boolean + default: false + isMerchantInitiated: + description: Specifies when the transaction is initiated by the merchant. + type: boolean + default: false + processedTime: + type: string + description: >- + Time the transaction is processed. + + This field is only specified if the transaction is processed + outside Rebilly. + format: date-time + description: Transaction resource. + required: true + PatchTransactionRequest: + content: + application/json: + schema: + type: object + title: Patch transaction request + properties: + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + description: Use the patch transaction request to modify custom fields. + required: true + CreditTransactionRequest: + content: + application/json: + schema: + type: object + required: + - websiteId + - customerId + - currency + - amount + properties: + websiteId: + $ref: '#/components/schemas/WebsiteId' + customerId: + $ref: '#/components/schemas/CustomerId' + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Amount of the transaction. + type: number + format: double + example: 97.97 + invoiceIds: + description: Array of invoice IDs. + type: + - array + - 'null' + items: + $ref: '#/components/schemas/ResourceId' + paymentInstruction: + description: |- + Payment instruction for the purchase. + If this value is not supplied, + the customer's default payment instrument is used. + $ref: '#/components/schemas/PaymentInstruction' + billingAddress: + description: >- + Billing address. + + If this value is not supplied, + + the billing address associated with the payment instrument is + used. + + If no billing address is associated with the payment + instrument, + + the customer's billing address is used. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + requestId: + description: >- + Use this field to prevent duplicate transaction requests that + may occur within a short period of time. + + If a duplicate request is sent with the same `requestId`, + + it is ignored to prevent double-billing. + + This value must be unique within a 24-hour period. + + + > **Important:** This field is recommended. + type: + - string + - 'null' + maxLength: 50 + pattern: '^[\-\w]+$' + example: 44433322-2c4y-483z-a0a9-158621f77a21 + gatewayAccountId: + description: >- + ID of the gateway account. + + Rebilly selects the payment gateway account for the + transaction based on transaction properties and the rules + configuration of the `gateway-account-requested` event. + + To prevent Rebilly from making the gateway account selection, + + supply a gateway account ID in this field. + + Only use this field if you intend to override the settings. + type: + - string + - 'null' + maxLength: 50 + example: gw_acc_0YVCXMF26DDNKAERE5NW727S34 + description: + type: + - string + - 'null' + description: Payment description. + maxLength: 255 + notificationUrl: + description: >- + URL where a server-to-server `POST` notification is sent. + + This notification is sent when the transaction result is + finalized after a timeout or an offsite interaction. + + + Do not interpret this notification as a confirmation, + + complete a `GET` request to confirm the result of the + transaction. + + To ensure the request is not reattempted, + + when the result is confirmed, respond with a `2xx` HTTP status + code. + + + The following placeholders are available to use in this URI: + `{id}` and `{result}`. + + These placeholders are replaced the with the transaction ID + and result accordingly. + type: + - string + - 'null' + format: uri + redirectUrl: + type: + - string + - 'null' + description: >- + URL to redirect the end-user when an offsite transaction is + completed. + + Defaults to the configured URL of the website. + + You may use `{id}` or `{result}` as placeholders in the URL, + + these are replaced the with the transaction ID and result + accordingly. + format: uri + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + isProcessedOutside: + description: Specifies when the transaction is processed outside Rebilly. + type: boolean + default: false + isMerchantInitiated: + description: Specifies when the transaction is initiated by the merchant. + type: boolean + default: false + processedTime: + type: string + description: >- + Time the transaction is processed. + + This field is only specified if the transaction is processed + outside Rebilly. + format: date-time + payoutRequestId: + description: >- + ID of the payout request for which the transaction is fully + allocated. + writeOnly: true + type: string + maxLength: 50 + description: Transaction resource. + required: true + ApiKey: + content: + application/json: + schema: + $ref: '#/components/schemas/ApiKey' + description: ApiKey resource. + required: true + PostServiceCredential: + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SmtpCredential' + - $ref: '#/components/schemas/WebhookCredential' + - $ref: '#/components/schemas/ExperianCredential' + - $ref: '#/components/schemas/MailgunCredential' + - $ref: '#/components/schemas/OAuth2Credential' + - $ref: '#/components/schemas/PostmarkCredential' + - $ref: '#/components/schemas/SendGridCredential' + - $ref: '#/components/schemas/SESCredential' + - $ref: '#/components/schemas/TaxJarCredential' + - $ref: '#/components/schemas/AvalaraCredential' + - $ref: '#/components/schemas/PlaidCredential' + description: Service credential resource. + required: true + PatchCredential: + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/PatchServiceCredential' + - $ref: '#/components/schemas/PatchPlaidCredential' + - $ref: '#/components/schemas/PatchTaxJarCredential' + description: Service credential resource. + required: true + RuleSet: + content: + application/json: + schema: + $ref: '#/components/schemas/RuleSet' + description: Ruleset resource. + required: true + RuleSetDraft: + content: + application/json: + schema: + $ref: '#/components/schemas/RuleSetDraft' + description: Draft ruleset resource. + required: true + GatewayAccount: + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccount' + description: Gateway Account resource. + required: true + GatewayAccountDowntimeSchedule: + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountDowntimeSchedule' + description: Gateway Account downtime schedule resource. + required: true + GridSegment: + content: + application/json: + schema: + $ref: '#/components/schemas/GridSegment' + description: Segment resource. + required: true + PostOrganization: + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Organization' + - required: + - name + - website + - country + - reportCurrency + description: Create organization request. + PatchOrganization: + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Organization' + - properties: + website: + readOnly: true + reportCurrency: + readOnly: true + description: Update Organization Request. + GlobalWebhook: + content: + application/json: + schema: + $ref: '#/components/schemas/GlobalWebhook' + description: Webhook resource. + required: true + User: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: User resource. + required: true + Website: + content: + application/json: + schema: + $ref: '#/components/schemas/Website' + description: Website resource. + required: true + DataExport: + content: + application/json: + schema: + $ref: '#/components/schemas/DataExport' + description: Data Export Request. + required: true + PatchAccountPassword: + content: + application/json: + schema: + required: + - currentPassword + - newPassword + properties: + currentPassword: + type: string + format: password + description: >- + Current password. This value is used to request a password + change. + newPassword: + type: string + format: password + writeOnly: true + description: New password. This value replaces the existing password. + PostDeposit: + content: + application/json: + schema: + type: object + required: + - depositRequestId + - amount + properties: + depositRequestId: + description: >- + ID of the deposit request. The created transaction is based on + the properties of this deposit request. + type: string + maxLength: 50 + example: 4f6cf35x-2c4y-483z-a0a9-158621f77a21 + amount: + description: Deposit amount. + type: number + format: double + example: 97.97 + properties: + description: Properties completed by the user in the deposit form. + type: object + additionalProperties: + type: string + example: + email: email@example.com + max: '33' + required: true + PostKycLivenessSession: + content: + application/json: + schema: + type: object + required: + - kycRequestId + properties: + kycRequestId: + type: string + description: >- + ID of the KYC request that the liveness session is associated + with. + maxLength: 50 + example: kyc_req_0YV7JMJ3DBCGRBR7K9D4HVGPP5 + description: KYC liveness session. + required: true + PatchOrder: + content: + application/json: + schema: + type: object + properties: + deliveryAddress: + description: Order delivery address. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + billingAddress: + description: Order billing address. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + description: Order resource. + required: true + PostOrderCancellation: + content: + application/json: + schema: + type: object + required: + - reason + properties: + reason: + description: Reason for the order cancellation. + type: string + enum: + - did-not-use + - did-not-want + - missing-features + - bugs-or-problems + - do-not-remember + - too-expensive + - other + description: + description: Description of the cancellation reason in free form. + type: string + maxLength: 255 + description: Order resource. + required: true + PostOrderPause: + content: + application/json: + schema: + type: object + properties: + description: + description: Description of the pause reason in free form. + type: + - string + - 'null' + maxLength: 255 + effectiveTime: + description: >- + Date and time when the service period pauses. + + + This time must be later than the current time. + + If this time is earlier then the current time, the current + time is used. + + + If this field is omitted, this value defaults to the current + time. + type: + - string + - 'null' + format: date-time + endTime: + description: >- + Date and time when the pause ends and the subscription resumes + billing. + + + To resume a subscription from this point in time, + + use the current time or an earlier time. + + If `endTime` is earlier then the current time, the current + time is used. + + If this field is empty, the subscription is indefinitely + paused. + type: + - string + - 'null' + format: date-time + description: Order resource. + required: true + PostPayment: + content: + application/json: + schema: + oneOf: + - type: object + title: For transaction + required: + - transactionId + properties: + transactionId: + $ref: '#/components/schemas/TransactionId' + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + redirectUrl: + $ref: '#/components/schemas/StorefrontTransactionRedirectUrl' + oneOf: + - $ref: '#/components/schemas/PaymentInstructionsToken' + - $ref: '#/components/schemas/PaymentInstructionsInstrument' + - type: object + title: For invoice + required: + - invoiceId + properties: + invoiceId: + type: string + description: Unique resource ID. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + redirectUrl: + $ref: '#/components/schemas/StorefrontTransactionRedirectUrl' + oneOf: + - $ref: '#/components/schemas/PaymentInstructionsToken' + - $ref: '#/components/schemas/PaymentInstructionsInstrument' + - type: object + title: For amount+currency + required: + - amount + - currency + - websiteId + properties: + websiteId: + $ref: '#/components/schemas/WebsiteId' + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Transaction amount. + type: number + format: double + default: 0 + example: 97.97 + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + redirectUrl: + $ref: '#/components/schemas/StorefrontTransactionRedirectUrl' + oneOf: + - $ref: '#/components/schemas/PaymentInstructionsToken' + - $ref: '#/components/schemas/PaymentInstructionsInstrument' + required: true + StorefrontPostPaymentInstrument: + content: + application/json: + schema: + type: object + required: + - token + properties: + token: + description: Token ID of the payment instrument. + type: string + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + description: Payment instrument. + required: true + StorefrontPatchPaymentInstrument: + content: + application/json: + schema: + type: object + properties: + token: + description: Token ID of the payment instrument. + type: string + billingAddress: + description: |- + Billing address. + If this field is supplied, + it overrides the billing address obtained from the token. + $ref: '#/components/schemas/ContactObject' + customFields: + $ref: '#/components/schemas/ResourceCustomFields' + description: Payment instrument. + required: true + SetupPaymentInstrumentRequest: + content: + application/json: + schema: + type: object + required: + - websiteId + - currency + properties: + websiteId: + $ref: '#/components/schemas/WebsiteId' + currency: + $ref: '#/components/schemas/CurrencyCode' + amount: + description: Amount of the transaction. + type: number + format: double + default: 0 + example: 97.97 + billingAddress: + description: >- + Billing address. + + If a billing address is not supplied the address associated + with the payment instrument is used. + + If no address is associated with the payment instrument, + + the customer's address is used. + oneOf: + - $ref: '#/components/schemas/ContactObject' + - type: 'null' + redirectUrl: + type: + - string + - 'null' + description: >- + URL to redirect the end-user when an offsite transaction is + completed. + + This field defaults to the configured website URL. + + You may use `{id}` or `{result}` as placeholders in the URL. + + These placeholders are replaced with the transaction ID and + result. + + For more information, see + [Placeholders](https://www.rebilly.com/docs/automations/email-notifications/#placeholders). + format: uri + riskMetadata: + $ref: '#/components/schemas/RiskMetadata' + description: Setup transaction resource. + required: true + PostRegister: + content: + application/json: + schema: + allOf: + - required: + - username + - password + properties: + username: + type: string + description: Username. + password: + type: string + format: password + description: Password. + primaryAddress: + $ref: '#/components/schemas/ContactObject' + paymentToken: + type: string + description: >- + Write-only payment token; + + if supplied, it converts into a payment instrument and is + set as the `defaultPaymentInstrument`. + + The value of this property overrides the + `defaultPaymentInstrument` in the case that both are + supplied. + + The token may only be used once before it is expired. + leadSource: + $ref: '#/components/schemas/LeadSourceData' + ApplicationInstanceDisabled: + content: + application/json: + schema: + properties: + organizationId: + $ref: '#/components/schemas/OrganizationId' + applicationId: + type: string + description: Unique application ID. + maxLength: 50 + example: app_01H0HT25X7XXCJGS4H8VJSYF2Y + eventType: + type: string + description: Type of webhook event. + enum: + - application-instance-disabled + _embedded: + description: Embedded objects. + type: object + properties: + application: + $ref: '#/components/schemas/Application' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - application + description: Application instance disabled webhook request body resource. + ApplicationInstanceEnabled: + content: + application/json: + schema: + properties: + organizationId: + $ref: '#/components/schemas/OrganizationId' + applicationId: + type: string + description: Unique application ID. + maxLength: 50 + example: app_01H0HT25X7XXCJGS4H8VJSYF2Y + eventType: + type: string + description: Type of webhook event. + enum: + - application-instance-enabled + _embedded: + description: Embedded objects. + type: object + properties: + application: + $ref: '#/components/schemas/Application' + applicationInstance: + $ref: '#/components/schemas/OwnerApplicationInstance' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - application + - applicationInstance + description: Application instance enabled webhook request body resource. + CustomerWebhook: + content: + application/json: + schema: + properties: + customerId: + $ref: '#/components/schemas/CustomerId' + eventType: + type: string + description: Type of webhook event. + enum: + - aml-list-possibly-matched + - customer-created + - customer-merged + - customer-one-time-password-requested + - customer-redirected-offsite + - customer-returned + - customer-updated + _embedded: + type: object + description: Embedded objects. + properties: + customer: + $ref: '#/components/schemas/Customer' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - customer + description: Customer webhook request body resource. + InvoiceRevenueRecognized: + content: + application/json: + schema: + properties: + recognitionId: + description: ID of the recognized revenue. + type: string + invoiceId: + type: string + description: ID of the invoice. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + itemId: + description: ID of the invoice item. + type: string + amount: + description: Amount recognized. + type: number + format: double + currency: + $ref: '#/components/schemas/CurrencyCode' + eventType: + type: string + description: Type of webhook event. + enum: + - invoice-revenue-recognized + _embedded: + type: object + description: >- + Available embedded objects that are requested by the expand + query parameter. + properties: + invoice: + $ref: '#/components/schemas/Invoice' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - invoice + description: Invoice webhook request body resource. + CouponRedemptionWebhook: + content: + application/json: + schema: + properties: + couponId: + description: ID of the coupon. + type: string + redemptionId: + description: ID of the redemption. + type: string + customerId: + description: ID of the customer. + type: string + eventType: + type: string + description: Type of webhook event. + enum: + - coupon-application-removed + - coupon-applied + - coupon-redeemed + - coupon-redemption-canceled + _embedded: + type: object + description: Embedded objects. + properties: + coupon: + $ref: '#/components/schemas/Coupon' + redemption: + $ref: '#/components/schemas/CouponRedemption' + customer: + $ref: '#/components/schemas/Customer' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - coupon + - redemption + - customer + description: Coupon redemption webhook request body resource. + CouponWebhook: + content: + application/json: + schema: + properties: + couponId: + description: ID of the coupon. + type: string + eventType: + type: string + description: Type of webhook event. + enum: + - coupon-expiration-modified + - coupon-expired + - coupon-issued + - coupon-modified + _embedded: + type: object + description: Embedded objects. + properties: + coupon: + $ref: '#/components/schemas/Coupon' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - coupon + description: Coupon webhook request body resource. + CreditMemoWebhook: + content: + application/json: + schema: + properties: + creditMemoId: + type: string + description: ID of the credit memo. + maxLength: 50 + example: crmm_0YVCNN22TWC3G8H82QNPNVZCHG + eventType: + type: string + description: Rebilly webhooks event type. + enum: + - credit-memo-applied + - credit-memo-created + - credit-memo-modified + - credit-memo-partially-applied + - credit-memo-voided + _embedded: + type: object + description: Embedded objects. + properties: + creditMemo: + $ref: '#/components/schemas/CreditMemo' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - creditMemo + description: Credit memo webhook request body resource. + MergedCustomer: + content: + application/json: + schema: + properties: + targetCustomerId: + description: ID of the customer that contains the merged data. + type: string + maxLength: 50 + example: cus_0YV7DDSDD1C8DA64KHH2W33CPF + eventType: + type: string + description: Type of webhook event. + enum: + - customer-merged + duplicatedCustomer: + $ref: '#/components/schemas/Customer' + _embedded: + type: object + description: Customer object. + properties: + targetCustomer: + $ref: '#/components/schemas/Customer' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - customer + description: Merged customer webhook request body resource. + CustomerRedirect: + content: + application/json: + schema: + properties: + transactionId: + $ref: '#/components/schemas/TransactionId' + redirectUrl: + description: URL customer redirected to. + type: string + format: url + ipAddress: + description: Customer's IP address. + type: string + httpHeaders: + description: Customer's browser HTTP headers. + type: object + additionalProperties: + type: string + example: + Content-Type: application/json + Accept: >- + text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 + eventType: + description: Rebilly webhooks event type. + type: string + enum: + - customer-redirected-offsite + - customer-returned + _embedded: + type: object + description: Embedded objects. + properties: + transaction: + $ref: '#/components/schemas/Transaction' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - transaction + description: Customer redirect webhook request body resource. + DisputeWebhook: + content: + application/json: + schema: + properties: + disputeId: + type: string + description: ID of the dispute. + maxLength: 50 + example: dp_0YVCE8J5F2DE58FV0S8YASW4HK + transactionId: + type: + - string + - 'null' + description: ID of the transaction. + maxLength: 50 + example: txn_0YVDTQJ8YWDGQACV2N2N5SPWQ0 + eventType: + type: string + description: Type of webhook event. + enum: + - dispute-created + - dispute-modified + _embedded: + type: object + description: Embedded objects. + properties: + dispute: + $ref: '#/components/schemas/Dispute' + transaction: + oneOf: + - $ref: '#/components/schemas/Transaction' + - type: 'null' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - dispute + - transaction + description: Dispute webhook request body resource. + ExperianCheckPerformed: + content: + application/json: + schema: + properties: + customerId: + $ref: '#/components/schemas/CustomerId' + outcome: + description: >- + Check decision where `1` - record matches, `0` - no matches + found, `-1` - record mismatches. + type: integer + enum: + - -1 + - 0 + - 1 + example: 1 + reason: + description: Text representation of the outcome. + type: string + example: >- + Individual has a 90 percent or greater match to forename + surname premise postcode with ID verified and DOB + errorCode: + description: Error code produced by the Experian system. + type: string + example: '001' + errorMessage: + description: Error message produced by the Experian system. + type: string + example: 'Authentication Error: Bad Username or Password' + eventType: + type: string + description: Type of webhook event. + enum: + - experian-check-performed + _embedded: + type: object + description: Embedded object. + properties: + customer: + $ref: '#/components/schemas/Customer' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - customer + description: Experian check performed webhook request body resource. + UsageLimitWebhook: + content: + application/json: + schema: + properties: + subscriptionId: + type: string + description: ID of the order. + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + subscriptionItemId: + type: string + description: ID of the order item. + example: ord_itm_01GYJPRKHBD6ZYHH897QCJMBS4 + planId: + type: string + description: ID of the plan. + example: plan_01GYJPRKHBD6ZYHH897QCJMBS4 + usageQuantity: + type: integer + description: Reported usage quantity. + usageAmount: + type: number + format: double + description: Reported usage amount. + currency: + type: string + description: Currency of the reported usage amount. + eventType: + type: string + description: Type of webhook event. + enum: + - soft-usage-limit-reached + - hard-usage-limit-reached + _embedded: + type: object + description: Embedded objects. + properties: + subscription: + $ref: '#/components/schemas/Subscription' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - subscription + description: Request body resource of the usage limit webhook. + InvoiceTaxCalculationFailed: + content: + application/json: + schema: + properties: + invoiceId: + type: string + description: ID of the invoice. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + reason: + description: Error message explaining tax calculation failure. + type: string + eventType: + type: string + description: Type of webhook event. + enum: + - invoice-tax-calculation-failed + _embedded: + type: object + description: Invoice object. + properties: + invoice: + $ref: '#/components/schemas/Invoice' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - invoice + description: Invoice tax validation failed webhook request body resource. + GatewayAccountWebhook: + content: + application/json: + schema: + properties: + gatewayAccountId: + type: string + description: ID of the gateway account. + maxLength: 50 + example: gw_acc_0YVCXMF26DDNKAERE5NW727S34 + eventType: + type: string + description: Type of webhook event. + enum: + - gateway-account-downtime-ended + - gateway-account-downtime-started + - gateway-account-onboarding-completed + - gateway-account-onboarding-failed + _embedded: + type: object + description: Embedded objects. + properties: + gatewayAccount: + $ref: '#/components/schemas/GatewayAccount' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - gatewayAccount + description: Gateway account limit reached body resource. + GatewayAccountAndGatewayAccountLimit: + content: + application/json: + schema: + properties: + gatewayAccountId: + type: string + description: ID of the gateway account. + maxLength: 50 + example: gw_acc_0YVCXMF26DDNKAERE5NW727S34 + eventType: + type: string + description: Type of webhook event. + enum: + - gateway-account-limit-reached + _embedded: + type: object + description: Embedded objects. + properties: + gatewayAccount: + $ref: '#/components/schemas/GatewayAccount' + gatewayAccountLimit: + $ref: '#/components/schemas/GatewayAccountLimit' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - gatewayAccount + - gatewayAccountLimit + description: Gateway account limit reached body resource. + TransactionWebhook: + content: + application/json: + schema: + properties: + transactionId: + $ref: '#/components/schemas/TransactionId' + eventType: + type: string + description: Type of webhook event. + enum: + - gateway-account-requested + - nsf-response-received + - offsite-payment-completed + - risk-score-changed + - transaction-amount-discrepancy-found + - transaction-declined + - transaction-discrepancy-found + - transaction-process-requested + - transaction-processed + - transaction-timeout-resolved + - waiting-gateway-transaction-completed + _embedded: + type: object + description: Embedded objects. + properties: + transaction: + $ref: '#/components/schemas/Transaction' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - transaction + description: Transaction webhook request body resource. + InvoiceWebhook: + content: + application/json: + schema: + properties: + invoiceId: + type: string + description: ID of the invoice. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + eventType: + type: string + description: Type of webhook event. + enum: + - invoice-abandoned + - invoice-created + - invoice-issued + - invoice-modified + - invoice-paid + - invoice-partially-paid + - invoice-partially-refunded + - invoice-past-due + - invoice-past-due-reminder + - invoice-refunded + - invoice-reissued + - invoice-tax-calculation-failed + - invoice-voided + _embedded: + type: object + description: >- + Available embedded objects that are requested by the expand + query parameter. + properties: + invoice: + $ref: '#/components/schemas/Invoice' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - invoice + description: Invoice webhook request body resource. + KycDocumentWebhook: + content: + application/json: + schema: + properties: + customerId: + $ref: '#/components/schemas/CustomerId' + kycDocumentId: + type: string + description: ID of the KYC document. + maxLength: 50 + example: kyc_doc_0YV7JHY705C6DA487BFTAA33V8 + fileId: + type: string + description: ID of the file. + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + eventType: + type: string + description: Type of webhook event. + enum: + - kyc-document-accepted + - kyc-document-created + - kyc-document-modified + - kyc-document-rejected + - kyc-document-reviewed + - kyc-document-archived + _embedded: + type: object + description: Customer and KYC document data. + properties: + customer: + $ref: '#/components/schemas/Customer' + kycDocument: + $ref: '#/components/schemas/KycDocument' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - customer + - file + - kycDocument + description: KYC document webhook request body resource. + KycRequestWebhook: + content: + application/json: + schema: + properties: + customerId: + $ref: '#/components/schemas/CustomerId' + kycRequestId: + type: string + description: ID of the KYC request. + maxLength: 50 + example: kyc_req_0YV7JMJ3DBCGRBR7K9D4HVGPP5 + eventType: + type: string + description: Rebilly webhooks event type. + enum: + - kyc-request-attempted + - kyc-request-failed + - kyc-request-fulfilled + - kyc-request-partially-fulfilled + _embedded: + type: object + description: Customer and KYC request data. + properties: + customer: + $ref: '#/components/schemas/Customer' + kycRequest: + $ref: '#/components/schemas/KycRequest' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - customer + - kycRequest + description: KYC request webhook body resource. + SubscriptionWebhook: + content: + application/json: + schema: + properties: + subscriptionId: + type: string + description: ID of the order. + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + eventType: + type: string + description: Type of webhook event. + enum: + - order-completed + - order-abandoned + - subscription-activated + - subscription-canceled + - subscription-churned + - subscription-downgraded + - subscription-modified + - subscription-reactivated + - subscription-renewal-reminder + - subscription-renewed + - subscription-trial-converted + - subscription-trial-end-changed + - subscription-trial-end-reminder + - subscription-trial-ended + - subscription-upgraded + _embedded: + type: object + description: Embedded objects. + properties: + subscription: + $ref: '#/components/schemas/Subscription' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - subscription + description: Order webhook request body resource. + PaymentCardWebhook: + content: + application/json: + schema: + properties: + paymentCardId: + description: ID of the payment instrument. + type: string + eventType: + type: string + description: Type of webhook event. + enum: + - payment-card-created + - payment-card-expiration-reminder + - payment-card-expired + _embedded: + type: object + description: Embedded objects. + properties: + paymentCard: + $ref: '#/components/schemas/PaymentCard' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - paymentCard + description: Payment card webhook request body resource. + PaymentInstrumentWebhook: + content: + application/json: + schema: + properties: + paymentInstrumentId: + type: string + description: ID of the payment instrument. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + eventType: + type: string + description: Type of webhook event. + enum: + - payment-instrument-modified + _embedded: + type: object + properties: + paymentInstrument: + $ref: '#/components/schemas/PaymentInstrument' + _links: + type: array + description: Related links. + readOnly: true + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - paymentInstrument + description: Payment instrument webhook request body resource. + PayoutRequestWebhook: + content: + application/json: + schema: + properties: + payoutRequestId: + description: ID of the payout request. + type: string + eventType: + type: string + description: Type of webhook event. + enum: + - payout-request-created + - payout-request-modified + _embedded: + type: object + description: Embedded objects. + properties: + payoutRequest: + $ref: '#/components/schemas/PayoutRequest' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - payoutRequest + description: Payout request webhook request body resource. + RenewalInvoiceIssued: + content: + application/json: + schema: + properties: + subscriptionId: + type: string + description: ID of the order. + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + invoiceId: + type: string + description: ID of the invoice. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + eventType: + type: string + description: Type of webhook event. + enum: + - renewal-invoice-issued + _embedded: + type: object + description: Embedded objects. + properties: + subscription: + $ref: '#/components/schemas/Subscription' + invoice: + $ref: '#/components/schemas/Invoice' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - subscription + - invoice + description: Renewal invoice issued webhook request body resource. + InvoiceAndTransaction: + content: + application/json: + schema: + properties: + transactionId: + $ref: '#/components/schemas/TransactionId' + invoiceId: + type: string + description: ID of the invoice. + maxLength: 50 + example: in_0YVF9605RKC62BP14NE2R7V2XT + eventType: + type: string + description: Type of webhook event. + enum: + - renewal-invoice-payment-canceled + - renewal-invoice-payment-declined + _embedded: + type: object + description: Embedded objects. + properties: + transaction: + $ref: '#/components/schemas/Transaction' + invoice: + $ref: '#/components/schemas/Invoice' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - transaction + - invoice + description: Invoice and Transaction webhook request body resource. + SubscriptionAndSubscriptionPause: + content: + application/json: + schema: + properties: + subscriptionId: + type: string + description: ID of the order. + example: ord_01GYJPRKHBD6ZYHH897QCJMBS4 + subscriptionPauseId: + type: string + description: ID of the subscription pause. + example: ord_pau_01H085J3ZR1WKD120D73D7N4C9 + eventType: + type: string + description: Type of webhook event. + enum: + - subscription-pause-created + - subscription-pause-modified + - subscription-pause-revoked + - subscription-paused + - subscription-resumed + _embedded: + type: object + description: Embedded objects. + properties: + subscription: + $ref: '#/components/schemas/Subscription' + pause: + $ref: '#/components/schemas/SubscriptionPause' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - subscription + - pause + description: Subscription pause and order webhook request body resource. + DataExportWebhook: + content: + application/json: + schema: + properties: + dataExportId: + type: string + description: ID of the data export. + maxLength: 50 + example: exp_0YVCNQ2C1AD1RA3HXKP492GNZB + fileId: + type: string + description: ID of the data export file. + maxLength: 50 + example: file_0YV7HZ7KDCC5WBV9Q7WRKG1H6N + eventType: + type: string + description: Rebilly webhooks event type. + enum: + - data-export-completed + _embedded: + type: object + description: Embedded objects. + properties: + dataExport: + $ref: '#/components/schemas/DataExport' + _links: + type: array + description: Related links. + items: + type: object + properties: + href: + description: Link URL. + type: string + rel: + description: Type of link. + type: string + enum: + - dataExport + - download + description: Data export webhook body resource. + examples: + stripe-trx-request: + summary: Stripe transaction fees + description: Transaction fees for Stripe. Discount rate fees are a separate record. + value: + type: buy + name: Stripe transaction fees + filter: 'type:sale,capture;result:approved;gatewayAccountId:stripe' + formula: + type: fixed-fee + currency: USD + amount: 0.3 + stripe-us-request: + summary: Stripe US fees + description: >- + Fees for Stripe in the US. Fees for outside of the US are a different + fee record. + value: + type: buy + name: Stripe US fees + filter: >- + type:sale,capture;result:approved;billingAddress.country:US;gatewayAccountId:stripe + formula: + type: percentage + currency: USD + bips: 290 + stripe-intl-request: + summary: Stripe international fees + description: Fees for Stripe outside of the US. + value: + type: buy + name: Stripe international fees + filter: >- + type:sale,capture;result:approved;billingAddress.country:!US;gatewayAccountId:stripe + formula: + type: percentage + currency: USD + bips: 390 + stripe-trx-response: + summary: Stripe transaction fees + description: Stripe transaction fees. Discount rate fees are a separate record. + value: + id: fee_01GQT145JX3XBRJ8K812Y3GRE9 + type: buy + name: Stripe transaction fees + filter: 'type:sale,capture;result:approved;gatewayAccountId:stripe' + formula: + type: fixed-fee + currency: USD + amount: 0.3 + createdTime: '2021-12-15T14:15:22Z' + updatedTime: '2021-12-15T14:15:22Z' + _links: + - rel: self + href: >- + https://api-sandbox.rebilly.com/fees/fee_01GQT145JX3XBRJ8K812Y3GRE9 + stripe-us-response: + summary: Stripe US fees + description: >- + Stripe transaction fees for the US. Fees for outside of the US are a + different fee record. + value: + id: fee_01GQT145JX3XBRJ8K812Y3GRE9 + type: buy + name: Stripe US fees + filter: >- + type:sale,capture;result:approved;billingAddress.country:US;gatewayAccountId:stripe + formula: + type: percentage + currency: USD + bips: 290 + createdTime: '2021-12-15T14:15:22Z' + updatedTime: '2021-12-15T14:15:22Z' + _links: + - rel: self + href: >- + https://api-sandbox.rebilly.com/fees/fee_01GQT145JX3XBRJ8K812Y3GRE9 + stripe-intl-response: + summary: Stripe international fees + description: Stripe transaction fees for outside of the US. + value: + id: fee_01GQT145JX3XBRJ8K812Y3GRE9 + type: buy + name: Stripe international fees + filter: >- + type:sale,capture;result:approved;billingAddress.country:!US;gatewayAccountId:stripe + formula: + type: percentage + currency: USD + bips: 390 + createdTime: '2021-12-15T14:15:22Z' + updatedTime: '2021-12-15T14:15:22Z' + _links: + - rel: self + href: >- + https://api-sandbox.rebilly.com/fees/fee_01GQT145JX3XBRJ8K812Y3GRE9 +tags: + - name: Account + description: >- + Use these operations to manage Rebilly accounts. + + An account in which you are subscribed to in order to use the Rebilly + product. + - name: Allowlists + description: >- + Use allowlists to exclude specific customer attribute data from risk score + checks. + + + Allowlists are lists of data that are excluded from risk score checks. + Allowlists prevent specific data from being added to a [blocklist + record](https://www.rebilly.com/docs/automations/blocklists/) when a [risk + score](https://www.rebilly.com/docs/automations/risk-scoring/) threshold + reached. + - name: AML + description: >- + Use Anti-Money Laundering (AML) operations to screen customers and help + prevent your business from becoming directly or indirectly involved in + criminal activity. + + + Use AML operations during customer creation, and some transaction + processing, + + to help determine if a potential customer (lead), or customer, has + political or economic sanctions against them. + + + AML operations search the following for screening purposes: + + Politically Exposed Persons (PEPs) lists, sanction lists, + + and adverse media lists. + - name: API keys + description: >- + Use API keys to identify and authenticate applications and users. + + Always keep your API keys private. + + When creating API keys, you can restrict them to a given set of + permissions. + + For information on how to create and manage API keys, see [API + keys](https://www.rebilly.com/docs/dev-docs/api-keys/). + - name: Application owners + description: >- + Use these operations to register applications to the Rebilly Apps Store + and manage application instances. + + An application owner is a person or organization that has submitted an app + to the Rebilly App Store. + + For more information, see [Submit an + app](https://www.rebilly.com/docs/dev-docs/submit-a-rebilly-app/). + - name: Application users + description: >- + Use these operations to install or uninstall apps from the Rebilly App + Store to your Rebilly account, and to manage application instances. + + An application user is a person or organization that uses an app that is + installed from the Rebilly App Store. + + For more information, see [Install or uninstall an + app](https://www.rebilly.com/docs/dev-docs/install-an-app/). + - name: Authentication + description: >- + Use these operations to manage the process of confirming the identity of + users that are attempting to access resources. + - name: Balance transactions + description: |- + Use these operations to view and manage balance transactions. + + > **Important:** These operations are experimental and may change. + - name: Billing portals + description: >- + Use these operations to create and manage billing portals. + + Rebilly hosted billing portals provide secure, Rebilly hosted pages, where + customers can: + + view invoices, cancel subscriptions, update payment instruments, and + update their address. + - name: Blocklists + description: >- + Use blocklists to prevent fraud and criminal activity. + + + Blocklists are lists of customer attribute values that are blocked from + buying from you. + + For example, if a customer attempts to make a purchase from you with a + credit card that is in a blocklist, the transaction is blocked and is not + processed. + + + Before a new transaction is processed in Rebilly, blocklists are examined + to check for attributes related to the entity. + + If a match is detected, the operation is aborted. + + A blocklist that expires after a period of time is called a greylist. + - name: Broadcast messages + description: >- + Use broadcast messages to notify customers and leads about upcoming + promotions, service updates, and events. + + Broadcast messages are emails that are sent to a specific group of + customers, or all customers. + + For more information, see [Create a broadcast + message](https://www.rebilly.com/docs/automations/email-notifications/#create-a-broadcast-messages). + - name: Checkout forms + description: >- + Use these operations to create and manage checkout forms. + + Rebilly hosted checkout forms provide secure and compliant checkouts. + + Checkout forms are customizable, and use fully responsive design, + + built-in error messaging, validation, and expedited checkout for returning + customers. + - name: Coupons + description: >- + Use coupons to reward customers, generate sales, or to test new pricing + strategies. + + Coupons enable you to apply different types of discounts to invoices, + subscriptions, and pricing plans. + + + Redeemed coupons are attached to a customer's account. + + Depending on the coupon restrictions, the redeemed coupons are then + applied from the customer's account to subsequent invoices or + subscriptions. + + Redeemed coupons can only be applied to invoices of the same currency. + + + Once a coupon is redeemed it cannot be modified. + + You may deactivate a coupon or create a new coupon, but you cannot reuse + the same coupon code. + + If you have a use case where you must reuse the same code, [contact + Rebilly](https://www.rebilly.com/contact/). + + + ### Coupon restriction types + + + The following coupon description types are available: + + + - `restrict-to-countries`: Restricts a coupon for use in specific + countries. + + - `discounts-per-redemption`: Restricts the number of times that the + coupon can be applied by one redemption. + For example, use this restriction to configure a coupon that can only be applied to the first subscription renewal of a particular product. + - `minimum-order-amount`: Sets a minimum order amount for a coupon + application. + For example, if the restriction amount is $20, the coupon is only applied to invoices with a total amount of $20 or more. + - `restrict-to-invoices`: Restricts a coupon to specific invoices. + + - `restrict-to-plans`: Restricts a coupon to specific pricing plans. + + - `restrict-to-subscriptions`: Restricts a coupon to specific order + subscriptions. + + - `restrict-to-customer-tags`: Restricts a coupon to customers with + specific tags. + + - `restrict-to-customers`: Restricts a coupon to specific customers. + + - `restrict-to-exclusive-application`: Restricts a coupon so that it + cannot be used in combination with other coupons. If more than one coupon + is active, a coupon with this restriction is only applied if it provides a + larger discount than the other coupons combined. If a coupon with this + restriction is applied, all other coupons are removed. + + - `restrict-to-products`: Restricts a coupon to specific products. + + - `paid-by-time`: Specifies a date and time at which a coupon redemption + expires if not paid. + + - `redemptions-per-customer`: Restricts the number of times that a coupon + can be redeemed by one customer. + For example, use this restriction to limit the number of redemptions to one per customer. + - `total-redemptions`: Restricts the number of times a coupon can be + redeemed by different customers. + + - `restrict-to-bxgy`: Provides "buy X get Y" promotions. + These promotions incentivize new customers to buy and also reward existing customers. + This restriction type enables you to add discounted plans to an invoice when a certain quantity of other plans are purchased. + For example, if a customer buys two or more books, they get one free bookmark. + - name: Credit memos + description: >- + Use credit memos to provide a customer with store credit. + + A common use case for using a credit memo is to provide a customer with + store credit, + + rather than a refund, if the customer pays more than they owe or returns a + product. + + For information on the credit memo resource, see + [Resources](https://www.rebilly.com/docs/dev-docs/concepts/#resources). + - name: Credit memos timeline + description: >- + Use credit memo timelines to maintain an audit trail of changes and + activity for each credit memo. + + Credit memos are a means of providing a customer with store credit. + - name: Custom domains + description: >- + Use custom domains to configure and use your own domain for forms and + billing portals, + + instead of the default Rebilly domain: `portal.secure-payments.app`. + + For more information, see [Configure a custom + domain](https://www.rebilly.com/docs/settings/organizations-and-websites#configure-a-custom-domain). + - name: Custom fields + description: >- + Use custom fields to extend a resource scheme to include custom data that + is not provided as a common field. + + Depending on the resource on which the custom field is added, it may be + available in the Rebilly UI. + + + Example: A custom field called `preferredCommunicationChannel` is added to + the customer resource. + + It has two allowed values, which are 'email' and 'phone'. + + + ```js + + { + "customFields": { + "preferredCommunicationChannel": "email" + } + } + + ``` + + + For detailed information on Rebilly resources, see + [Resources](https://www.rebilly.com/docs/dev-docs/concepts/#resources). + - name: Customer authentication + description: >- + Use these operations to validate the identity of users and manage + authentication credentials. + - name: Customers + description: >- + Use these operations to manage customers. + + A customer is an entity that purchases goods or services from you (a + merchant), + + and is the payee in any transaction that is credited to you. + + Customers are associated with payment instruments, + + subscriptions, invoices, and other related resources. + + + In other systems, customers may be referred to as accounts, + + clients, members, patrons, or players. + + For information on the customer resource, see + [Resources](https://www.rebilly.com/docs/dev-docs/concepts/#resources). + - name: Customers timeline + description: >- + Use customer timelines to maintain an audit trail of changes and activity + for each customer. + - name: Data exports + description: >- + Use data export operations to manage the export of resource data, such as: + + transactions, customers, subscriptions, invoices, invoice item data, or + revenue audit. + + Common data export use cases are: + + accounting, data analysis, reporting, or importing into other databases. + + + For detailed information on Rebilly resources, + + see + [Resources](https://www.rebilly.com/docs/dev-docs/concepts/#resources). + + + For information on how manage reconciliation, see [Transaction + reconciliation](https://www.rebilly.com/docs/dev-docs/transaction-reconciliation). + - name: Deposits + description: >- + Create and manage deposit requests and manage strategies that determine + the deposit amounts to display on the page. + + Rebilly hosted deposit form provides a secure and compliant way to deposit + funds. + + Deposit forms are customizable, and use fully responsive design, built-in + error messaging and validation. + + + > **Important:** These operations are experimental and may change. + - name: Disputes + description: >- + Use these operations to manage disputes. + + A dispute occurs when a customer contests a charge to their account. + + The dispute and related information is made available to the merchant by + the bank or credit card company. + + The merchant then has the option to represent the charge and win the case. + + This process is called dispute resolution. + + If the merchant is unable to represent the charge, the card issuer + typically reverses the sale and adds fees on top of the charge. + + This process is called a chargeback. + - name: Email delivery settings + description: >- + Use email delivery settings to configure from which email address + notifications and broadcast message are sent. + + In Rebilly, this email address is referred to as a "From address". + + Supported email service providers are: + + SMTP, Mailgun, SendGrid, AWS SES, Postmark, and Rebilly. + - name: Email messages + description: Use email messages to email customers directly. + - name: Email notifications + description: >- + Use email notifications to keep customers informed on events, + + and to inform them of events that may require action on their end. + + Email notifications are targeted email messages that can be plain text or + HTML. + + + Email notifications can also be used to notify teammates about new + customers, blocklist matches, risk score changes, and more. + + For more information, see [Email + notifications](https://www.rebilly.com/docs/automations/email-notifications/). + - name: Email verification + description: >- + Use these operations to manage email verification. + + Email verification is the process of confirming that an email address + exists and is associated with an individual. + - name: External identifiers + description: >- + Use external identifier operations to associate entities such as + customers, + + invoices, transactions, journal accounts, journal entries, + + and more with external services. + + + To use external identifiers, you must use an active service credential. + - name: Fees + description: >- + Use fees to reconcile transactions with applicable fees and discount + rates. + + Fees are not applied directly to transaction amounts, they do not modify + the transaction amount. + + Fees help to describe each part of the transaction amount. + + + > **Important:** These operations are experimental and may change. + - name: Files + description: >- + Use the file entity to store files and related metadata. + + Files can be sorted by size, MIME-type, user-defined tags, and + description. + + + The following methods are available to upload files: + + multipart/form-data encoded form, RAW POST — + + by sending the file contents as the POST body, and fetching from URL + — + + by providing the file URL using the 'url' parameter. + + + Use the attachments entity to link a file to one or multiple objects, such + as: + + customer, dispute, transaction, order, plan, product, invoice, or timeline + comment. + + Attachments enable you to quickly find and use files related to specific + entities. + - name: Gateway accounts + description: >- + Use these operations to manage payment gateway accounts. + + A payment gateway is a service which enables merchants to receive payments + from their customers to their merchant account. + + A merchant account is a bank account that enables businesses to receive + payments. + + Use payment gateway accounts to connect payment requests to third party + networks and platforms. + - name: Gateway accounts timeline + description: >- + Use gateway account timelines to maintain an audit trail of changes and + activity for each gateway account. + - name: Histograms + description: >- + Use histograms operations to generate transaction histogram reports with + cohorts and periods. For information on the invoice resource, see + [Resources](https://www.rebilly.com/docs/dev-docs/concepts/#resources). + - name: Integrations + description: >- + Use these operations to manage third-party apps that are integrated to + your Rebilly account. + + For more information on third-party apps, and how to integrate them, + + see [App store](https://www.rebilly.com/docs/app-store/app-store/). + - name: Invoices + description: >- + Use invoices to bill for the goods or services that you provide. + + If your invoice includes subscription items, it also includes the + corresponding service periods and prices. + - name: Invoices timeline + description: >- + Use invoice timelines to maintain an audit trail of changes and activity + for each invoice. + - name: Journal + description: 'Use journal accounts, entries and records to track and recognize revenue.' + - name: KYC documents + description: >- + Use Know Your Customer (KYC) documents to verify your customers identity. + + The KYC document operations generate a signed link to the Rebilly [KYC + document + gatherer](https://www.rebilly.com/docs/kyc-and-aml/kyc-document-gatherer/). + + + Document types: + - `identity-proof`: Validates a customer's identity. + - `address-proof`: Validates a customer's address. + - `purchase-proof`: Validates a customer's purchase. + - `funds-proof`: Validates that a customer has funds. + - `credit-file-proof`: Verifies that there is an existing credit file with two sources that match the customer's name, DOB, and address. + + Rebilly parses and analyzes the documents and accepts or rejects them + according to a configurable scoring algorithm. + + When all document types in a KYC request are accepted, the status is + fulfilled, and the [KYC request fulfilled + webhook](https://www.rebilly.com/docs/dev-docs/api/operation/kyc-request-fulfilled/) + is sent. + + + ### Credit file proof + + + The `credit-file-proof` KYC document request type is only available in the + API, + + this option is not available in the [KYC document + gatherer](https://www.rebilly.com/docs/kyc-and-aml/kyc-document-gatherer/). + + This request verifies that there is an existing credit file with two + sources that match the person's name, date of birth, and address. + + + In Canada, to meet FINTRAC requirements, the `credit-file-proof` KYC + document request verifies the customer's name, address, and date of birth + with a credit agency. + + + If `credit-file-proof` is requested in combination with `identity-proof` + and `address-proof`, + + `credit-file-proof` is attempted first. + + If `credit-file-proof` validates the identity and address, + + the KYC request is considered fulfilled. + + If the request is not fulfilled, + + redirect your customer to the KYC document gatherer to collect their KYC + documents. + - name: Lists + description: >- + Use lists to create and maintain sets of values that may be referenced in + rule conditions. + + Commonly used lists contain values related to conditions that target + specific properties such as: customers, transactions, or BINs. + + + You may grant list edit permissions to members of your team without + enabling them to edit rules. + + This may be useful if your workflow involves frequent updates to value + sets that are used in rule or bind conditions. + + List order may not be maintained and duplicates may be removed. + + + For more information, see [Value + lists](https://www.rebilly.com/docs/automations/create-a-value-list/#value-lists). + - name: Memberships + description: |- + Use these operations to manage user membership within your organizations. + Membership describes the relationship between organizations and users. + For example, one user could be a member of more than one organization. + - name: Metadata + description: |- + Use these operations to retrieve and manage metadata. + Metadata is structured information on other data. + - name: Orders + description: >- + Use these operations to manage customer orders. + + An order is a customer's request to purchase items. + + It can contain subscription and one-time sale items. + + When an order contains one or more subscription items, it is a + subscription order. + + + An order generates an invoice. + + A subscription order generates an invoice for each service period. + + For more information, see + [Orders](https://www.rebilly.com/docs/dev-docs/concepts/#orders). + - name: Orders timeline + description: >- + Use order timelines to maintain an audit trail of changes and activity for + each order. + - name: Organization data exports + description: >- + Use organization data exports to export your organization data which you + may want to do as a business continuity practice. + + These may also be used to to export data if you decide to migrate to a + different payments solution. + - name: Organizations + description: >- + Use these operations to manage your organizations in Rebilly. + + An organization is an entity that represents you, or your company, as a + merchant. + + You can have multiple organizations. + + For more information, see [My organizations and + websites](https://www.rebilly.com/docs/settings/organizations-and-websites/). + + + Note: No data, including organizations, is shared between the live and + sandbox environments. + - name: Password reset + description: Use these operations to manage password resets for a user or organization. + - name: Payment instruments + description: >- + Use these operations to manage payment instruments. + + Payment instrument is a term which describes any means of making a digital + payment, such as: + + credit cards, debit cards, direct debits, payment service providers, and + digital wallets. + + + For more information on payment instruments, see [Payment + instruments](https://www.rebilly.com/docs/dev-docs/concepts/#payment-instruments). + - name: Payment tokens + description: >- + Use payment tokens to reduce the scope of [PCI DSS + compliance](https://www.rebilly.com/docs/pci-compliance/). + + + A payment token can be made using a different authentication scheme + (public key authentication), which enables you to create a payment token + directly from the browser. + + This bypasses the need to send sensitive cardholder info to your servers. + + We recommend using this with the FramePay library, which helps you + integrate a form into this API resource and create payment tokens. + - name: Plans + description: >- + Use pricing plans to describe how the customer must pay for + [products](https://www.rebilly.com/docs/dev-docs/api/tag/Products/). + + + Rebilly provides the following plan types: + + + - Trial only: Use this plan to create and offer a free or discounted trial + period for your product. + For example, a free 2 week trial of an internet service. + After the trial, the customer can choose to sign up for a paid subscription, or stop using the service. + - Recurring: Use this plan to create and offer a subscription for your + product. + For example, a monthly subscription to an internet service that is charged at 20 USD per month. + - One time sale: Use this plan to create and offer a one-off sale for your + products. + For example, a one time purchase of two bags of coffee. + + For information on the plans resource, see + [Plans](https://www.rebilly.com/docs/dev-docs/concepts/#plans). + + + For information on plan pricing, see [Pricing + formulas](https://www.rebilly.com/docs/settings/pricing-formulas/). + - name: Products + description: >- + Use products to describe the goods and services that your business sells. + + A product also describes sales items on invoices and receipts. + + Product pricing is defined in + [plans](https://www.rebilly.com/docs/dev-docs/api/tag/Plans/). + + A product may have one or many plans. + + + For information on the product resource, see + [Product](https://www.rebilly.com/docs/dev-docs/concepts/#products). + - name: Profile + description: |- + Use these operations to manage user profiles. + A profile represents the person that is signed in to Rebilly. + - name: Purchases + description: >- + Use these operations to manage purchases. + + Purchases are transactions that have been executed related to the purchase + of goods or services. + - name: Quotes + description: >- + Use quote operations to create and manage quotations. + + Quotations describe the cost of goods or services to potential customers + before they commit to a purchase. + + A quote contains an initial invoice preview that can be accepted to become + an order. + + + A quote can contain subscription and one-time sale items. + + When a quote contains one or more subscription items, it is a subscription + order quote. + - name: Quotes timeline + description: >- + Use quote timelines to maintain an audit trail of changes and activity for + each quote. + - name: Reports + description: >- + Use reports operations to retrieve summary information about your + customers, subscriptions, transactions, and more. + - name: Reset password + description: >- + Use these operations to manage password resets. + + When a password reset is requested, an email with a token is sent. + + In the email the user must click a reset link, and then create a new + password. + - name: Risk score + description: >- + Use risk score operations to configure blocklists based on transaction + risk factors. + - name: Roles + description: >- + Use these operations to manage user roles within your team or + organization. + + Roles are an implementation of the general hierarchical (Role Based Access + Control) RBAC. + + A senior role inherits all of its juniors' Access Control Lists (ACLs) and + its own ACL. + + Junior roles are not influenced by the senior role. + - name: Rules + description: >- + Use rules to automate. + + In the Rebilly rules engine, an action is an operation that executes when + an event occurs. + + Action and event conditions are defined in rules. + + Rulesets are collections of rules that are associated with an event. + + The order in which the rules are placed within a ruleset specifies the + execution order. + + + For a list of events and related actions, see + [Events](https://www.rebilly.com/docs/automations/event-types/). + - name: Rules timeline + description: >- + Use rule timelines to maintain an audit trail of changes and activity for + each rule. + - name: Search + description: >- + Use search operations to search data on customers, invoices, orders, + transactions and more. + - name: Segments + description: >- + Use these operations to manage UI segments. + + A segment is a filtered view of a data set that can be shared with other + users. + + For example, you may create a transactions segment that displays specific + columns that are filtered and sorted based on certain criteria, + + and share it with others in your organization. + - name: Service credentials + description: >- + Use these operations to get, create, and manage credentials for + third-party services. + - name: Shipping rates + description: >- + Use these operations to manage shipping rates. + + A shipping rate contains a filter and a pricing for a specific shipping + destination. + - name: Status + description: |- + Use this operation to check the status of the Rebilly API. + No authentication is required. + - name: Tags + description: >- + Use tags to organize and categorize customers or KYC documents based on + keywords. + - name: Tracking + description: |- + Use tracking when debugging and auditing issues. + Tracking is a layer for accessing all activity such as: + API requests, subscriptions, webhooks, events, and more. + - name: Transactions + description: |- + Use these operations to: + - set up payment instruments for payments + - authorize and hold funds + - capture funds + - make payments + - make payouts + - refund transactions. + - name: Transactions timeline + description: >- + Use transaction timelines to maintain an audit trail of changes and + activity for each transaction. + - name: Usage + description: >- + Use these operations to manage the product usage of a subscription item + for metered billing purposes. + + + Use metered billing when product quantity is unknown to the customer at + the moment of creating a subscription. + + Metered billing is based on reported usage records. + + Every reported usage updates the quantity of an upcoming invoice item for + a specified subscription and a plan. + + To create a metered billing plan, see [Plans](../Plans). + - name: Users + description: >- + Use these operations to manage users. + + A user is a person who can login to Rebilly, and take actions based on + their granted permissions. + - name: Webhooks + description: >- + Use these operations to manage webhooks. + + Webhooks notify your systems in real-time when certain events occur. + + For example, when a new transaction occurs or a new subscription is + created. + + Webhooks enable you to collect information about events. + + Rebilly can send this information by HTTP POST request to a defined URL of + your choice. + - name: Websites + description: >- + Use these operations to manage websites. + + A website is where your organization obtains a customer. + + It is also the processor account and billing descriptor used for payment + transactions. + + You can create multiple websites. + + + Websites are related to each invoice and each payment gateway account. + + This enables you to associate gateway accounts with multiple websites, + + or make them exclusive to particular websites. + + For more information, see [My organizations and + websites](https://www.rebilly.com/docs/settings/organizations-and-websites/). +paths: + /aml-checks: + get: + x-products: + - Core + tags: + - AML + summary: Retrieve AML checks + operationId: GetAmlCheckCollection + x-sdk-operation-name: getAll + description: >- + Retrieves a list of AML checks. + + These checks are records of customer data and potentially matching data + in AML lists. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of AML checks retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/AmlCheck' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/aml-checks/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - AML + summary: Retrieve an AML check + operationId: GetAmlCheck + x-sdk-operation-name: get + description: |- + Retrieves the results of a customer's AML check. + Customer metadata and an array of matching AML hits is returned. + responses: + '200': + description: AML check retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/AmlCheck' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/aml-checks/{id}/start-review': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - AML + summary: Start review of an AML check + operationId: PostAmlCheckStartReview + x-sdk-operation-name: startReview + description: >- + Starts the manual review process for an AML check with a specified ID. + + + This operation also sets the AML check `reviewStartTime` to the current + date-time, + + and updates the review information. + responses: + '201': + description: AML check review started. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/AmlCheck' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + '/aml-checks/{id}/stop-review': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - AML + summary: Stop review of an AML check + operationId: PostAmlCheckStopReview + x-sdk-operation-name: stopReview + description: >- + Stops the manual review process for an AML check with a specified ID. + + + This operation also sets the AML check `reviewStartTime` and the + reviewer information to null. + + Use this operation when the reviewer must stop the review. + + For example, if the reviewer must take a break, or ends a shift. + responses: + '201': + description: AML document review stopped. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/AmlCheck' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + '/aml-checks/{id}/review': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - AML + summary: Review an AML check + operationId: PostAmlCheckReview + x-sdk-operation-name: review + description: >- + Reviews an AML check. + + The AML check can be either confirmed or marked as a false positive with + a customer tag. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AmlCheckReview' + responses: + '201': + description: AML check reviewed. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/AmlCheck' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + /aml-settings: + get: + x-products: + - Core + tags: + - AML + summary: Retrieve AML settings + operationId: GetAmlSettings + x-sdk-operation-name: getAmlSettings + description: Retrieves AML settings. + responses: + '200': + description: AML settings retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/AmlSettings' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + tags: + - AML + summary: Update AML settings + operationId: PutAmlSettings + x-sdk-operation-name: putAmlSettings + description: Updates AML settings. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AmlSettings' + description: Settings for AML searches. + required: true + responses: + '200': + description: AML settings updated. + content: + application/json: + schema: + $ref: '#/components/schemas/AmlSettings' + '201': + description: AML settings created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/AmlSettings' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /attachments: + get: + x-products: + - Core + tags: + - Files + summary: Retrieve attachments + operationId: GetAttachmentCollection + x-sdk-operation-name: getAllAttachments + description: |- + Retrieves a list of attachments. You may sort attachments by: `id`, + `name`, `relatedId`, `relatedType`, `fileId`, `createdTime`, + and `updatedTime`. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionExpand' + - $ref: '#/components/parameters/collectionFields' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of attachments retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Attachment' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $attachments = $client->attachments()->search([ + 'filter' => 'relatedType:customer', + ]); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.files.getAllAttachments(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.files.getAllAttachments(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(attachment => + console.log(attachment.fields.relatedType)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $attachmentsPaginator = + $service->files()->getAllAttachmentsPaginator(limit: 5, filter: + 'relatedType:customer'); + + foreach ($attachmentsPaginator as $attachmentPage) { + printf("Attachments page %d/%d\n", $attachmentsPaginator->key() + 1, $attachmentsPaginator->count()); + foreach ($attachmentPage as $attachment) { + printf("Attachment #%s (%s): %s\n", $attachment->getId(), $attachment->getRelatedType(), $attachment->getName()); + } + } + + + // OR + + + $attachments = $service->files()->getAllAttachments(filter: + 'relatedType:customer'); + + foreach ($attachments as $attachment) { + printf("Attachment #%s (%s): %s\n", $attachment->getId(), $attachment->getRelatedType(), $attachment->getName()); + } + post: + x-products: + - Core + tags: + - Files + summary: Create an attachment + operationId: PostAttachment + x-sdk-operation-name: attach + description: |- + Attaches a file to one or multiple objects, such as: customer, + dispute, transaction, order, plan, product, invoice, or timeline + comment. Attachments enable you to quickly find and use files + related to specific entities. + requestBody: + $ref: '#/components/requestBodies/Attachment' + responses: + '201': + description: Attachment created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Attachment' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $attachmentForm = new Rebilly\Entities\Attachment(); + $attachmentForm->setFileId('fileId'); + $attachmentForm->setRelatedType($attachmentForm::TYPE_CUSTOMER); + $attachmentForm->setRelatedId('customerId'); + + try { + $attachment = $client->attachments()->create($attachmentForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: | + // Define the attachment + const data = { + // Previously uploaded file ID + fileId: 'my-file-id', + relatedType: 'customer', + relatedId: 'my-customer-id', + name: 'an attachment', + description: `the customer's file` + }; + + const attachment = await api.files.attach({data}); + console.log(attachment.fields.id); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $attachmentForm = new \Rebilly\Sdk\Model\Attachment(); + + $attachmentForm->setFileId('fileId'); + + $attachmentForm->setRelatedType($attachmentForm::RELATED_TYPE_CUSTOMER); + + $attachmentForm->setRelatedId('customerId'); + + + try { + $attachment = $service->files()->attach($attachmentForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/attachments/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Files + summary: Retrieve an attachment + operationId: GetAttachment + x-sdk-operation-name: getAttachment + description: Retrieves an attachment with a specified ID. + responses: + '200': + description: Attachment retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Attachment' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $attachment = $client->attachments()->load('attachmentId'); + - lang: JavaScript + source: >- + const attachment = await api.files.getAttachment({id: + 'foobar-001'}); + + console.log(attachment.fields.relatedType); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $attachment = $service->files()->getAttachment('attachmentId'); + put: + x-products: + - Core + tags: + - Files + summary: Update an attachment + operationId: PutAttachment + x-sdk-operation-name: updateAttachment + description: Updates an attachment with a specified ID. + requestBody: + $ref: '#/components/requestBodies/Attachment' + responses: + '200': + description: Attachment updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Attachment' + '201': + description: Attachment created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Attachment' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $attachmentForm = new Rebilly\Entities\Attachment(); + $attachmentForm->setFileId('fileId'); + $attachmentForm->setRelatedType($attachmentForm::TYPE_CUSTOMER); + $attachmentForm->setRelatedId('customerId'); + + try { + $attachment = $client->attachments()->update('attachmentId', $attachmentForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // define the values to update + + const data = { + relatedType: 'customer', + relatedId: 'my-customer-id', + name: 'an attachment', + description: `the customer's file` + }; + + + const attachment = await api.files.updateAttachment({id: + 'foobar-001', data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $attachmentForm = new \Rebilly\Sdk\Model\Attachment(); + + $attachmentForm->setFileId('fileId'); + + $attachmentForm->setRelatedType($attachmentForm::RELATED_TYPE_CUSTOMER); + + $attachmentForm->setRelatedId('customerId'); + + + try { + $attachment = $service->files()->updateAttachment('attachmentId', $attachmentForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Core + tags: + - Files + summary: Delete an attachment + operationId: DeleteAttachment + x-sdk-operation-name: detach + description: Deletes an attachment with a specified ID. + responses: + '204': + description: Attachment deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->attachments()->delete('attachmentId'); + - lang: JavaScript + source: |- + const request = await api.files.detach({id: 'my-attachment-id'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $service->files()->detach('attachmentId'); + /authentication-options: + get: + x-products: + - Core + tags: + - Customer authentication + summary: Retrieve authentication options + operationId: GetAuthenticationOption + x-sdk-operation-name: getAuthOptions + description: Retrieves customer authentication options. + responses: + '200': + description: Authentication options retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/AuthenticationOptions' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $authenticationOptions = $client->authenticationOptions()->load(); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $authenticationOptions = + $service->customerAuthentication()->getAuthOptions(); + - lang: JavaScript + source: |- + const options = await api.customerAuthentication.getAuthOptions(); + console.log(options.fields.credentialTtl); + put: + x-products: + - Core + tags: + - Customer authentication + summary: Change authentication options + operationId: PutAuthenticationOption + x-sdk-operation-name: updateAuthOptions + description: Changes customer authentication options. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AuthenticationOptions' + description: Authentication options resource. + required: true + responses: + '200': + description: Authentication options updated. + content: + application/json: + schema: + $ref: '#/components/schemas/AuthenticationOptions' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $authenticationOptionsForm = new + Rebilly\Entities\AuthenticationOptions(); + + // Regular expression below matches any password with 6+ length that + contains alphabet symbols and/or numbers. + + $authenticationOptionsForm->setPasswordPattern('/^[a-zA-Z0-9]{6,}$/'); + + + try { + $authenticationOptions = $client->authenticationOptions()->update($authenticationOptionsForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the properties for the authentication options + + const data = { + passwordPattern: null, + credentialTtl: 10, + authTokenTtl: 20, + resetTokenTtl: 30 + }; + + + const options = await + api.customerAuthentication.updateAuthOptions({data}); + + console.log(options.fields.credentialTtl); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $authenticationOptionsForm = new + \Rebilly\Sdk\Model\AuthenticationOptions(); + + // Regular expression below matches any password with 6+ length that + contains alphabet symbols and/or numbers. + + $authenticationOptionsForm->setPasswordPattern('/^[a-zA-Z0-9]{6,}$/'); + + + try { + $authenticationOptions = $service->customerAuthentication()->updateAuthOptions($authenticationOptionsForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + /authentication-tokens: + get: + x-products: + - Core + tags: + - Customer authentication + summary: Retrieve authentication tokens + operationId: GetAuthenticationTokenCollection + x-sdk-operation-name: getAllAuthTokens + description: Retrieves a list of customer authentication tokens. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: List of auth tokens retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/AuthenticationTokenResponse' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $authenticationTokens = $client->authenticationTokens()->search([ + 'filter' => 'customerId:testCustomer', + ]); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $listOfTokens = + $service->customerAuthentication()->getAllAuthTokens(); + + + // alternatively you can specify one or more of them + + $listOfTokens = + $coreService->customerAuthentication()->getAllAuthTokens(limit: 5, + offset: 2); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await + api.customerAuthentication.getAllAuthTokens(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100}; + + const secondCollection = await + api.customerAuthentication.getAllAuthTokens(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(token => + console.log(token.fields.username)); + post: + x-products: + - Core + tags: + - Customer authentication + summary: Login a customer + operationId: PostAuthenticationToken + x-sdk-operation-name: login + description: Logs in a customer. + security: + - SecretApiKey: [] + - JWT: [] + - PublishableApiKey: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AuthenticationToken' + description: AuthenticationToken resource. + required: true + responses: + '201': + description: Login successful. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/AuthenticationTokenResponse' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $authenticationForm = new Rebilly\Entities\AuthenticationToken(); + $authenticationForm->setUsername('username'); + $authenticationForm->setPassword('test123'); + + try { + $authenticationToken = $client->authenticationTokens()->login($authenticationForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: |- + const data = { + username: 'foobar', + password: 'fuubar' + + // optionally you can define an `expiredTime` to + // limit the duration of the session token + + //expiredTime: '2017-09-18T19:17:39Z' + }; + const session = await api.customerAuthentication.login({data}); + console.log(session.fields.token); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $authenticationForm = + \Rebilly\Sdk\Model\AuthenticationTokenPasswordMode::from(); + + $authenticationForm->setUsername('username'); + + $authenticationForm->setPassword('test123'); + + + try { + $authenticationToken = $service->customerAuthentication()->login($authenticationForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/authentication-tokens/{token}': + parameters: + - name: token + in: path + description: ID of the authentication token. + required: true + schema: + type: string + get: + x-products: + - Core + tags: + - Customer authentication + summary: Verify an authentication token + operationId: GetAuthenticationTokenVerification + x-sdk-operation-name: verify + description: Verifies a customer's authentication token. + security: + - SecretApiKey: [] + - JWT: [] + - PublishableApiKey: [] + responses: + '200': + description: Authentication token verified. + content: + application/json: + schema: + $ref: '#/components/schemas/AuthenticationTokenResponse' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $isVerified = $client->authenticationTokens()->verify('token'); + - lang: JavaScript + source: >- + const token = 'dcf6e32f2daee457a1db8ce5fdfbe200'; + + const verification = await + api.customerAuthentication.verify({token}); + + // if the the token is valid then no error will be thrown + + console.log(verification.reponse.status) // 200 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $isVerified = $service->customerAuthentication()->verify('token'); + delete: + x-products: + - Core + tags: + - Customer authentication + summary: Logout a customer + operationId: DeleteAuthenticationToken + x-sdk-operation-name: logout + description: Logs out a customer. + security: + - SecretApiKey: [] + - JWT: [] + - PublishableApiKey: [] + responses: + '204': + description: Customer logged out. + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->authenticationTokens()->logout('token'); + - lang: JavaScript + source: |- + const token = 'dcf6e32f2daee457a1db8ce5fdfbe200'; + const request = await api.customerAuthentication.logout({token}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $service->customerAuthentication()->logout('token'); + '/authentication-tokens/{token}/exchange': + parameters: + - name: token + in: path + description: ID of the authentication token. + required: true + schema: + type: string + post: + x-products: + - Core + tags: + - Customer authentication + summary: Exchange an authentication token + operationId: PostAuthenticationTokenExchange + x-sdk-operation-name: exchangeToken + description: >- + Exchanges an authentication token for a JWT. + + + + By default, this operation invalidates the exchanged authentication + token. + security: + - SecretApiKey: [] + - JWT: [] + - PublishableApiKey: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerJWT' + required: true + responses: + '201': + description: Authentication token exchanged for a JWT. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerJWT' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /blocklists: + get: + x-products: + - Core + tags: + - Blocklists + summary: Retrieve blocklists + operationId: GetBlocklistCollection + x-sdk-operation-name: getAll + description: Retrieves a list of all blocklists. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of blocklists retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Blocklist' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $blocklists = $client->blocklists()->search([ + 'filter' => 'value:testValue', + ]); + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await api.blocklists.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.blocklists.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(blocklistItem => + console.log(blocklistItem.fields.status)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $blocklistsPaginator = + $service->blocklists()->getAllPaginator(limit: 5, filter: + 'value:testValue'); + + foreach ($blocklistsPaginator as $blocklistPage) { + printf("Blocklists page %d/%d\n", $blocklistsPaginator->key() + 1, $blocklistsPaginator->count()); + foreach ($blocklistPage as $blocklist) { + printf("Blocklist #%s (%s): %s\n", $blocklist->getId(), $blocklist->getType(), $blocklist->getValue()); + } + } + + + // OR + + + $blocklists = $service->blocklists()->getAll(filter: + 'value:testValue'); + + foreach ($blocklists as $blocklist) { + printf("Blocklist #%s (%s): %s\n", $blocklist->getId(), $blocklist->getType(), $blocklist->getValue()); + } + post: + x-products: + - Core + tags: + - Blocklists + summary: Create a blocklist + operationId: PostBlocklist + x-sdk-operation-name: create + description: Creates a blocklist. + requestBody: + $ref: '#/components/requestBodies/Blocklist' + responses: + '201': + description: Blocklist created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Blocklist' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $blocklistForm = new Rebilly\Entities\Blocklist(); + $blocklistForm->setType($blocklistForm::TYPE_EMAIL); + $blocklistForm->setValue('test@test.com'); + $blocklistForm->setExpiredTime('2025-01-01 05:00:00'); + + try { + $blocklist = $client->blocklists()->create($blocklistForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // first set the required properties for the new blocklist item + + const data = { + type: 'ip-address', + value: '63.118.98.100' + + // optionally provide an `expirationTime` to make + // the item expire and function like a `greylist` + + // expirationTime: '2017-09-18T21:50:44Z' + }; + + + // the ID is optional + + const firstKey = await api.blocklists.create({data}); + + + // or you can provide one + + const secondKey = await api.blocklists.create({id: 'my-second-id', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $blocklistForm = new \Rebilly\Sdk\Model\Blocklist(); + $blocklistForm->setType($blocklistForm::TYPE_EMAIL); + $blocklistForm->setValue('test@test.com'); + $blocklistForm->setExpirationTime('2025-01-01 05:00:00'); + + try { + $blocklist = $service->blocklists()->create($blocklistForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/blocklists/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Blocklists + summary: Retrieve a blocklist + operationId: GetBlocklist + x-sdk-operation-name: get + description: Retrieves a blocklist with a specified ID. + responses: + '200': + description: Blocklist retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Blocklist' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $blocklist = $client->blocklists()->load('blocklistId'); + - lang: JavaScript + source: | + const blocklistItem = await api.blocklists.get({id: 'foobar-001'}); + console.log(blocklistItem.fields.status); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $blocklist = $service->blocklists()->get('blocklistId'); + put: + x-products: + - Core + tags: + - Blocklists + summary: Create a blocklist with specified ID + operationId: PutBlocklist + x-sdk-operation-name: update + description: Creates a blocklist with a specified ID. + requestBody: + $ref: '#/components/requestBodies/Blocklist' + responses: + '200': + description: Blocklist updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Blocklist' + '201': + description: Blocklist created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Blocklist' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $blocklistForm = new Rebilly\Entities\Blocklist(); + $blocklistForm->setType($blocklistForm::TYPE_EMAIL); + $blocklistForm->setValue('test@test.com'); + $blocklistForm->setExpiredTime('2025-01-01 05:00:00'); + + try { + $blocklist = $client->blocklists()->create($blocklistForm, 'blocklistId'); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // first set the required properties for the new blocklist item + + const data = { + type: 'ip-address', + value: '63.118.98.100' + + // optionally provide an `expirationTime` to make + // the item expire and function like a `greylist` + + // expirationTime: '2017-09-18T21:50:44Z' + }; + + + // the ID is optional + + const firstKey = await api.blocklists.create({data}); + + + // or you can provide one + + const secondKey = await api.blocklists.create({id: 'my-second-id', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $blocklistForm = new \Rebilly\Sdk\Model\Blocklist(); + $blocklistForm->setType($blocklistForm::TYPE_EMAIL); + $blocklistForm->setValue('test@test.com'); + $blocklistForm->setExpirationTime('2025-01-01 05:00:00'); + + try { + $blocklist = $service->blocklists()->update('blocklistId', $blocklistForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Core + tags: + - Blocklists + summary: Delete a blocklist + operationId: DeleteBlocklist + x-sdk-operation-name: delete + description: Deletes a blocklist with a specified ID. + responses: + '204': + description: Blocklist deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->blocklists()->delete('blocklistId'); + - lang: JavaScript + source: | + const request = await api.blocklists.delete({id: 'my-second-key'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $service->blocklists()->delete('blocklistId'); + /coupons-redemptions: + get: + x-products: + - Core + tags: + - Coupons + summary: Retrieve coupon redemptions + operationId: GetCouponRedemptionCollection + x-sdk-operation-name: getAllRedemptions + description: Retrieves a list of coupon redemptions. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of coupon redemptions retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CouponRedemption' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $couponRedemptions = $client->couponsRedemptions()->search([ + 'filter' => 'customerId:testCustomer', + ]); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.coupons.getAllRedemptions(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await + api.coupons.getAllRedemptions(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(coupon => + console.log(coupon.fields.status)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $couponRedemptionsPaginator = + $service->coupons()->getAllRedemptionsPaginator(limit: 5, filter: + 'customerId:testCustomer'); + + foreach ($couponRedemptionsPaginator as $couponRedemptionsPage) { + printf("Coupon redemptions page %d/%d\n", $couponRedemptionsPaginator->key() + 1, $couponRedemptionsPaginator->count()); + foreach ($couponRedemptionsPage as $couponRedemption) { + printf("Coupon redemption #%s\n", $couponRedemption->getId()); + } + } + + + // OR + + + $couponRedemptions = $service->coupons()->getAllRedemptions(filter: + 'customerId:testCustomer'); + + foreach ($couponRedemptions as $couponRedemption) { + printf("Coupon redemption #%s\n", $couponRedemption->getId()); + } + post: + x-products: + - Core + tags: + - Coupons + summary: Redeem a coupon + operationId: PostCouponRedemption + x-sdk-operation-name: redeem + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Redeems a coupon. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CouponRedemption' + description: Coupon resource. + required: true + responses: + '201': + description: Coupon redeemed. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CouponRedemption' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $redemptionForm = new Rebilly\Entities\Coupons\Redemption(); + + $redemptionForm->setCustomerId('customerId'); + + $redemptionForm->setCouponId('couponId'); + + + $restrictionData = [ + 'type' => Rebilly\Entities\Coupons\Restriction::TYPE_DISCOUNTS_PER_REDEMPTION, + 'quantity' => 2, + ]; + + + $restrictionForm = + Rebilly\Entities\Coupons\Restriction::createFromData($restrictionData); + + + $redemptionForm->setAdditionalRestrictions([$restrictionForm]); + + + try { + $couponRedemption = $client->couponsRedemptions()->redeem($redemptionForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: | + // build the redemption data + const data = { + couponId: 'my-best-coupon', + customerId: 'foobar-001' + }; + + const redemption = await api.coupons.redeem({data}); + console.log(redemption.fields.id); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $redemptionForm = new \Rebilly\Sdk\Model\CouponRedemption(); + + $redemptionForm->setCustomerId('customerId'); + + $redemptionForm->setCouponId('couponId'); + + + $restrictionForm = new + \Rebilly\Sdk\Model\CouponRestrictionDiscountPerRedemption(); + + $restrictionForm->setQuantity(2); + + + $redemptionForm->setAdditionalRestrictions([$restrictionForm]); + + + try { + $couponRedemption = $service->coupons()->redeem($redemptionForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/coupons-redemptions/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Coupons + summary: Retrieve a coupon redemption + operationId: GetCouponRedemption + x-sdk-operation-name: getRedemption + description: Retrieves a coupon redemption with a specified ID. + responses: + '200': + description: Coupon redemption retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/CouponRedemption' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $couponRedemption = + $client->couponsRedemptions()->load('redemptionId'); + - lang: JavaScript + source: >- + const redemption = await api.coupons.getRedemption({id: + 'foobar-001'}); + + console.log(redemption.fields.id); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $couponRedemption = + $service->coupons()->getRedemption('redemptionId'); + '/coupons-redemptions/{id}/cancel': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Coupons + summary: Cancel a coupon redemption + operationId: PostCouponRedemptionCancellation + x-sdk-operation-name: cancelRedemption + description: Cancels a coupon redemption with a specified ID. + responses: + '201': + description: Coupon redemption cancelled. + headers: + Location: + $ref: '#/components/headers/Location' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->couponsRedemptions()->cancel('id'); + - lang: JavaScript + source: >- + const cancellation = await api.coupons.cancelRedemption({id: + 'foobar-001'}); + + + // the request does not return any fields but + + // you can confirm the success using the status code + + console.log(cancellation.response.status); // 201 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $service->coupons()->cancelRedemption('id'); + /coupons: + get: + x-products: + - Core + tags: + - Coupons + summary: Retrieve coupons + operationId: GetCouponCollection + x-sdk-operation-name: getAll + description: Retrieves a list of coupons. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of coupons retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Coupon' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $coupons = $client->coupons()->search([ + 'filter' => 'status:issued', + ]); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.coupons.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.coupons.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(coupon => + console.log(coupon.fields.status)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $couponsPaginator = $service->coupons()->getAllPaginator(limit: 5, + filter: 'status:issued'); + + foreach ($couponsPaginator as $couponsPage) { + printf("Coupons page %d/%d\n", $couponsPaginator->key() + 1, $couponsPaginator->count()); + foreach ($couponsPage as $coupon) { + printf("Coupon #%s (%s): %s\n", $coupon->getId(), $coupon->getStatus(), $coupon->getDescription()); + } + } + + + // OR + + + $coupons = $service->coupons()->getAll(filter: 'status:issued'); + + foreach ($coupons as $coupon) { + printf("Coupon #%s (%s): %s\n", $coupon->getId(), $coupon->getStatus(), $coupon->getDescription()); + } + post: + x-products: + - Core + tags: + - Coupons + summary: Create a coupon + operationId: PostCoupon + x-sdk-operation-name: create + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Creates a coupon. + requestBody: + $ref: '#/components/requestBodies/Coupon' + responses: + '201': + description: Coupon created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Coupon' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $couponForm = new Rebilly\Entities\Coupons\Coupon(); + + + $discountArray = [ + 'currency' => 'USD', + 'amount' => 1.99, + ]; + + + $discountForm = new + \Rebilly\Entities\Coupons\Discounts\Fixed($discountArray); + + $couponForm->setDiscount($discountForm); + + // Coupon can be used right now + + $couponForm->setIssuedTime(date('c')); + + + $restrictionArray = [ + 'quantity' => 2, + ]; + + + $restrictionForm = new + Rebilly\Entities\Coupons\Restrictions\DiscountsPerRedemption($restrictionArray); + + + $couponForm->setRestrictions([$restrictionForm]); + + + try { + $coupon = $client->coupons()->create($couponForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // first set the required properties for the new coupon + + const data = { + description: 'a new coupon', + issuedTime: '2017-09-19T20:46:44Z', + discount: { + type: 'percent', + value: 12 + }, + restrictions: [{ + type: 'discounts-per-redemption', + quantity: 12 + }] + }; + + + // the ID is optional + + const firstKey = await api.coupons.create({data}); + + + // or you can provide one + + const secondKey = await api.coupons.create({id: 'my-second-id', + data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $couponForm = new \Rebilly\Sdk\Model\Coupon(); + + + $discountForm = new \Rebilly\Sdk\Model\DiscountFixed(); + + $discountForm->setCurrency('USD'); + + $discountForm->setAmount(1.99); + + + $couponForm->setDiscount($discountForm); + + // Coupon can be used right now + + $couponForm->setIssuedTime(date('c')); + + + $restrictionArray = [ + 'quantity' => 2, + ]; + + + $restrictionForm = new + \Rebilly\Sdk\Model\CouponRestrictionDiscountPerRedemption($restrictionArray); + + + $couponForm->setRestrictions([$restrictionForm]); + + + try { + $coupon = $service->coupons()->create($couponForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/coupons/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Coupons + summary: Retrieve a coupon + operationId: GetCoupon + x-sdk-operation-name: get + description: Retrieves a coupon with a specified ID. + responses: + '200': + description: Coupon retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Coupon' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $coupon = $client->coupons()->load('couponId'); + - lang: JavaScript + source: | + const coupon = await api.coupons.get({couponId: 'foobar-001'}); + console.log(coupon.fields.status); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $coupon = $service->coupons()->get('couponId'); + put: + x-products: + - Core + tags: + - Coupons + summary: Upsert a coupon + operationId: PutCoupon + x-sdk-operation-name: update + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Creates or updates (upserts) a coupon with a specified coupon ID. + requestBody: + $ref: '#/components/requestBodies/Coupon' + responses: + '200': + description: Coupon updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Coupon' + '201': + description: Coupon created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Coupon' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $couponForm = new Rebilly\Entities\Coupons\Coupon([ + 'issuedTime' => '2022-01-01T00:00:00-04:00', + ]); + + + $discountArray = [ + 'type' => Rebilly\Entities\Coupons\Discount::TYPE_FIXED, + 'currency' => 'USD', + 'amount' => 1.99, + ]; + + + $discountForm = + Rebilly\Entities\Coupons\Discount::createFromData($discountArray); + + $couponForm->setDiscount($discountForm); + + + $restrictionArray = [ + 'type' => Rebilly\Entities\Coupons\Restriction::TYPE_DISCOUNTS_PER_REDEMPTION, + 'quantity' => 2, + ]; + + + $restrictionForm = + Rebilly\Entities\Coupons\Restriction::createFromData($restrictionArray); + + + $couponForm->setRestrictions([$restrictionForm]); + + + try { + $coupon = $client->coupons()->create($couponForm, 'couponId'); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // creating a coupon + + const data = { + description: 'a new coupon', + issuedTime: '2017-09-19T20:46:44Z', + discount: { + type: 'percent', + value: 12 + }, + restrictions: [{ + type: 'discounts-per-redemption', + quantity: 12 + }] + }; + + + // the ID is optional + + const firstKey = await api.coupons.create({data}); + + + // or you can provide one + + const secondKey = await api.coupons.create({id: 'my-second-id', + data}); + + + + // updating a coupon + + const data = { + description: 'a small update' + }; + + + const coupon = await api.coupons.update({id: 'my-second-id', data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $couponForm = new \Rebilly\Sdk\Model\Coupon(); + + $couponForm->setIssuedTime('2022-01-01T00:00:00-04:00'); + + + $discountForm = new \Rebilly\Sdk\Model\DiscountFixed(); + + $discountForm->setCurrency('USD'); + + $discountForm->setAmount(1.99); + + + $couponForm->setDiscount($discountForm); + + + $restrictionForm = new + \Rebilly\Sdk\Model\CouponRestrictionDiscountPerRedemption(); + + $restrictionForm->setQuantity(2); + + + $couponForm->setRestrictions([$restrictionForm]); + + + try { + $coupon = $service->coupons()->update('couponId', $couponForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/coupons/{id}/expiration': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Coupons + summary: Specify a coupon expiration time + operationId: PostCouponExpiration + x-sdk-operation-name: setExpiration + description: |- + Specifies the expiration time of a coupon with a specified ID. + The `expiredTime` of a coupon must be greater than its `issuedTime`. + + > **Note:** This operation cannot be performed on expired coupons. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CouponExpiration' + description: Coupon resource. + responses: + '201': + description: Coupon expiration set. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Coupon' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + description: |- + Coupon has expired and been redeemed. Unable to reschedule + expiration. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: > + const data = { + expiredTime: "2020-05-25T18:51:14Z" + } + + + const coupon = await api.coupons.setExpiration({id: 'my-second-id', + data}); + /credentials: + get: + x-products: + - Core + tags: + - Customer authentication + summary: Retrieve credentials + operationId: GetCredentialCollection + x-sdk-operation-name: getAllCredentials + description: Retrieves a list of authentication credentials. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: List of authentication credentials retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CustomerCredential' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $customerCredentials = $client->customerCredentials()->search([ + 'filter' => 'customerId:testCustomer', + ]); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $customerCredentials = + $service->customerAuthentication()->getAllCredentials(limit: 10); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await + api.customerAuthentication.getAllCredentials(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100}; + + const secondCollection = await + api.customerAuthentication.getAllCredentials(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(credential => + console.log(credential.fields.customerId)); + post: + x-products: + - Core + tags: + - Customer authentication + summary: Create a credential + operationId: PostCredential + x-sdk-operation-name: createCredential + description: Creates an authentication credential. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerCredential' + description: Credential resource. + required: true + responses: + '201': + description: Credential created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerCredential' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $customerCredentialForm = new Rebilly\Entities\CustomerCredential(); + $customerCredentialForm->setCustomerId('customerId'); + $customerCredentialForm->setUsername('test'); + $customerCredentialForm->setPassword('1234'); + + try { + $customerCredential = $client->customerCredentials()->create($customerCredentialForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the required properties for the new credential + + const data = { + username: 'foobar', + password: 'fuubar', + customerId: 'foobar-0001' + + // optionally you can define an `expiredTime` to + // limit the duration of the credential + + //expiredTime: '2017-09-18T19:17:39Z' + }; + + + // the ID is optional + + const firstCredential = await + api.customerAuthentication.createCredential({data}); + + + // or you can provide one + + const secondCredential = await + api.customerAuthentication.createCredential({id: 'my-second-id', + data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $customerCredentialForm = + \Rebilly\Sdk\Model\CustomerCredential::from([]) + ->setCustomerId('customerId') + ->setUsername('test') + ->setPassword('1234'); + + try { + $customerCredential = $service->customerAuthentication()->createCredential($customerCredentialForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/credentials/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Customer authentication + summary: Retrieve a credential + operationId: GetCredential + x-sdk-operation-name: getCredential + description: Retrieves a credential with a specified ID. + responses: + '200': + description: Credential retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerCredential' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $customerCredential = + $client->customerCredentials()->load('credentialId'); + - lang: JavaScript + source: >- + const credential = await + api.customerAuthentication.getCredential({id: 'my-first-id'}); + + console.log(credential.fields.customerId); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $customerCredential = + $service->customerAuthentication()->getCredential('credentialId'); + put: + x-products: + - Core + tags: + - Customer authentication + summary: Upsert a credential + operationId: PutCredential + x-sdk-operation-name: updateCredential + description: >- + Creates or updates (upserts) an authentication credential with a + specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerCredential' + description: Credential resource. + required: true + responses: + '200': + description: Credential updated. + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerCredential' + '201': + description: Credential created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerCredential' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $customerCredentialForm = new Rebilly\Entities\CustomerCredential(); + $customerCredentialForm->setCustomerId('customerId'); + $customerCredentialForm->setUsername('test'); + $customerCredentialForm->setPassword('1234'); + + try { + $customerCredential = $client->customerCredentials()->update('credentialId', $customerCredentialForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // creating a new credential + + const data = { + username: 'foobar', + password: 'fuubar', + customerId: 'foobar-0001' + + // optionally you can define an `expiredTime` to + // limit the duration of the credential + + //expiredTime: '2017-09-18T19:17:39Z' + }; + + + // the ID is optional + + const firstCredential = await + api.customerAuthentication.createCredential({data}); + + + // or you can provide one + + const secondCredential = await + api.customerAuthentication.createCredential({id: 'my-second-id', + data}); + + + + + + // updating a credential + + const data = { + username: 'foobar', + password: 'hell0' + }; + + + const secondCredential = await + api.customerAuthentication.updateCredential({id: 'my-second-id', + data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $customerCredentialForm = + \Rebilly\Sdk\Model\CustomerCredential::from([]) + ->setCustomerId('customerId') + ->setUsername('test') + ->setPassword('1234'); + + try { + $customerCredential = $service->customerAuthentication()->updateCredential('credentialId', $customerCredentialForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Core + tags: + - Customer authentication + summary: Delete a credential + operationId: DeleteCredential + x-sdk-operation-name: deleteCredential + description: Deletes an authentication credential with a specified ID. + responses: + '204': + description: Credential deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->customerCredentials()->delete('credentialId'); + - lang: JavaScript + source: >- + const request = await + api.customerAuthentication.deleteCredential({id: 'my-second-key'}); + + + // the request does not return any fields but + + // you can confirm the success using the status code + + console.log(request.response.status); // 204 + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $service->customerAuthentication()->deleteCredential('credentialId'); + '/custom-fields/{resource}': + parameters: + - $ref: '#/components/parameters/customFieldResource' + get: + x-products: + - Core + tags: + - Custom fields + summary: Retrieve custom fields + operationId: GetCustomFieldCollection + x-sdk-operation-name: getAll + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + description: Retrieves the custom fields schema for a specified resource. + responses: + '200': + description: Schema of custom fields retrieved. + content: + application/json: + schema: + description: List of custom fields. + type: array + items: + $ref: '#/components/schemas/CustomField' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $customFields = $client->customFields()->search('customers', [ + 'filter' => 'type:boolean', + ]); + - lang: JavaScript + source: >- + // all parameters except `resource` are optional + + const firstCollection = await api.customFields.getAll({resource: + 'customers'}); + + + // alternatively you can specify one or more of them + + const params = {resource: 'customers', limit: 20, offset: 100}; + + const secondCollection = await api.customFields.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(customField => + console.log(customField.fields.description)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $customFieldsPaginator = + $service->customFields()->getAllPaginator(resource: 'customers', + limit: 5); + + foreach ($customFieldsPaginator as $customFieldPage) { + printf("Custom fields page %d/%d\n", $customFieldsPaginator->key() + 1, $customFieldsPaginator->count()); + foreach ($customFieldPage as $customField) { + printf("Custom field #%s: %s\n", $customField->getName(), $customField->getDescription()); + } + } + + + // OR + + + $customFields = $service->customFields()->getAll('customers'); + + foreach ($customFields as $customField) { + printf("Custom field #%s: %s\n", $customField->getName(), $customField->getDescription()); + } + '/custom-fields/{resource}/{name}': + parameters: + - $ref: '#/components/parameters/customFieldResource' + - name: name + in: path + description: ID of the custom field. + required: true + schema: + type: string + maxLength: 60 + pattern: '^[\w-]+$' + get: + x-products: + - Core + tags: + - Custom fields + summary: Retrieve a custom field + operationId: GetCustomField + x-sdk-operation-name: get + description: Retrieves a specified custom field schema for a specified resource. + responses: + '200': + description: Custom field schema retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/CustomField' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $customField = $client->customFields()->load('customers', + 'testFieldName'); + - lang: JavaScript + source: >- + const customField = await api.customFields.get({resource: + 'customers', name: 'dob'}); + + console.log(customField.fields.description); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $customField = $service->customFields()->get('customers', + 'testFieldName'); + put: + x-products: + - Core + tags: + - Custom fields + summary: Create or alter a custom field + operationId: PutCustomField + x-sdk-operation-name: create + description: >- + Creates or alters a specified custom field schema for a specified + resource. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CustomField' + description: Custom fields schema of the specified resource. + required: true + responses: + '200': + description: Custom field updated. + content: + application/json: + schema: + $ref: '#/components/schemas/CustomField' + '201': + description: Custom field created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CustomField' + '401': + $ref: '#/components/responses/Unauthorized' + '409': + description: |- + Schema in use. To remove or alter the schema, remove all + associated data. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $customFieldForm = new Rebilly\Entities\CustomField(); + $customFieldForm->setType($customFieldForm::TYPE_BOOLEAN); + + try { + $customField = $client->customFields()->update('customers', 'testFieldName', $customFieldForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: | + // creating a custom field + const data = { + name: 'Date of Birth', + type: 'date', + description: `The customer's date of birth` + }; + + // define the entire payload + const params = {resource: 'customers', name: 'dob', data}; + + // create the custom field + const customField = await api.customFields.create(params); + + // or update the custom field + const customField = await api.customFields.update(params); + + // you can verify if the custom field is used + console.log(customField.fields.isUsed); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $customFieldForm = new \Rebilly\Sdk\Model\BooleanCustomField(); + $customFieldForm->setDescription('testFieldDescription'); + + try { + $customField = $service->customFields()->create('customers', 'testFieldName', $customFieldForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + /customers: + get: + x-products: + - Core + tags: + - Customers + summary: Retrieve customers + operationId: GetCustomerCollection + x-sdk-operation-name: getAll + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a list of customers. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionExpand' + - $ref: '#/components/parameters/collectionFields' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of customers retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Customer' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $customers = $client->customers()->search([ + 'filter' => 'firstName:John', + ]); + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await api.customers.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.customers.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(customer => + console.log(customer.fields.primaryAddress.firstName)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $customersPaginator = $service->customers()->getAllPaginator(limit: + 5); + + foreach ($customersPaginator as $customerPage) { + printf("Customers page %d/%d\n", $customersPaginator->key() + 1, $customersPaginator->count()); + foreach ($customerPage as $customer) { + printf("Customer #%s: %s\n", $customer->getId(), $customer->getFirstName()); + } + } + + + // OR + + + $customers = $service->customers()->getAll(filter: + 'firstName:John'); + + foreach ($customers as $customer) { + printf("Customer #%s: %s\n", $customer->getId(), $customer->getFirstName()); + } + post: + x-products: + - Core + tags: + - Customers + summary: Create a customer + operationId: PostCustomer + x-sdk-operation-name: create + description: >- + Creates a new customer and customer ID. + + + The customer's primary address is used as the default address for + + payment instruments, subscriptions, and invoices if none are provided. + + + If the customer already has an identifier within your system, + + and you want to create customer with a predefined ID — to + + prevent duplicate customers, use the _Upsert a customer + + with predefined ID_ operation. For more information, see + + [Prevent duplicate + customers](https://www.rebilly.com/docs/dev-docs/concepts/#prevent-duplicate-customers). + requestBody: + $ref: '#/components/requestBodies/Customer' + responses: + '201': + description: Customer created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $customerForm = new Rebilly\Entities\Customer(); + $customerForm->setPrimaryAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + try { + $customer = $client->customers()->create($customerForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the properties for the new customer + + const data = { + primaryAddress: { + firstName: 'John', + lastName: 'Doe', + emails: [{ + label: 'main', + value: 'john.doe+test@grr.la', + primary: true + }], + } + }; + + + // the ID is optional + + const firstCustomer = await api.customers.create({data}); + + + // or you can provide one + + const secondCustomer = await api.customers.create({id: + 'my-second-id', data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $customerForm = \Rebilly\Sdk\Model\Customer::from([]) + ->setWebsiteId('websiteId') + ->setPrimaryAddress( + \Rebilly\Sdk\Model\ContactObject::from([]) + ->setFirstName('John') + ->setLastName('Doe') + ->setAddress('Test street 5') + ->setEmails([ + \Rebilly\Sdk\Model\ContactEmails::from([]) + ->setLabel('main') + ->setValue('johndoe@email.com') + ->setPrimary(true), + ]), + ); + + try { + $customer = $service->customers()->create($customerForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/customers/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Customers + summary: Retrieve a customer + operationId: GetCustomer + x-sdk-operation-name: get + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a customer with a specified ID. + parameters: + - $ref: '#/components/parameters/collectionExpand' + - $ref: '#/components/parameters/collectionFields' + responses: + '200': + description: Customer retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $customers = $client->customers()->load('myCustomerId'); + - lang: JavaScript + source: |- + const customer = await api.customers.get({id: 'foobar-001'}); + console.log(customer.fields.primaryAddress.firstName); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $customer = $service->customers()->get('customerId'); + put: + x-products: + - Core + tags: + - Customers + summary: Upsert a customer + operationId: PutCustomer + x-sdk-operation-name: update + description: >- + Creates or updates (upserts) a customer with a specified ID. + + + If the customer already has an identifier within your system, + + and you want to create a customer with a specified ID — use this + operation to prevent duplicate customers. + + For more information, see [Prevent duplicate + customers](https://www.rebilly.com/docs/dev-docs/concepts/#prevent-duplicate-customers). + requestBody: + $ref: '#/components/requestBodies/Customer' + responses: + '200': + $ref: '#/components/responses/Customer' + '201': + description: Customer with a specified ID created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $customerForm = new Rebilly\Entities\Customer(); + $customerForm->setPrimaryAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + try { + $customer = $client->customers()->update('myCustomerId', $customerForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // creating a customer + + const data = { + primaryAddress: { + firstName: 'John', + lastName: 'Doe', + emails: [{ + label: 'main', + value: 'john.doe+test@grr.la', + primary: true + }], + } + }; + + + // the ID is optional + + const firstCustomer = await api.customers.create({data}); + + + // or you can provide one + + const secondCustomer = await api.customers.create({id: + 'my-second-id', data}); + + + + + // updating a customer + + const data = { + primaryAddress: { + firstName: 'Johnny', + lastName: 'Doe', + emails: [{ + label: 'main', + value: 'johnny.doe+test@grr.la', + primary: true + }], + } + }; + + + const customer = await api.customers.update({id: 'my-second-id', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $customerForm = \Rebilly\Sdk\Model\Customer::from([]) + ->setWebsiteId('websiteId') + ->setPrimaryAddress( + \Rebilly\Sdk\Model\ContactObject::from([]) + ->setFirstName('John') + ->setLastName('Doe') + ->setAddress('Test street 5') + ->setEmails([ + \Rebilly\Sdk\Model\ContactEmails::from([]) + ->setLabel('main') + ->setValue('johndoe@email.com') + ->setPrimary(true), + ]), + ); + + try { + $customer = $service->customers()->update('myCustomerId', $customerForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Core + parameters: + - in: query + name: targetCustomerId + required: true + schema: + type: string + description: |- + ID of the customer that you want to delete. This field is used + to obtain the customer's data. + tags: + - Customers + summary: Merge and delete a customer + operationId: DeleteCustomer + x-sdk-operation-name: merge + description: >- + Merges one duplicate customer to another target customer and deletes the + former. + responses: + '204': + description: Customer is merged and deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $service->customers()->merge('customerId', 'targetCustomerId'); + /customer-timeline-custom-events: + get: + x-products: + - Core + tags: + - Customers timeline + summary: Retrieve customer timeline custom event types + operationId: GetCustomerTimelineCustomEventTypeCollection + x-sdk-operation-name: getAll + description: Retrieves a list of customer timeline custom event types. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: List of customer timeline custom event types retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CustomerTimelineCustomEvent' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Customers timeline + summary: Create customer timeline custom event type + operationId: PostCustomerTimelineCustomEventType + x-sdk-operation-name: create + description: Creates a customer timeline custom event type. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerTimelineCustomEvent' + description: Customer timeline custom event type resource. + required: true + responses: + '201': + description: Customer timeline custom event type created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerTimelineCustomEvent' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/customer-timeline-custom-events/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Customers timeline + summary: Retrieve customer timeline custom event type + operationId: GetCustomerTimelineCustomEventType + x-sdk-operation-name: get + description: Retrieves a customer timeline custom event type with a specified ID. + responses: + '200': + description: Customer timeline custom event type retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerTimelineCustomEvent' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/customers/{id}/lead-source': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Customers + summary: Retrieve a customer's lead source + operationId: GetCustomerLeadSource + x-sdk-operation-name: getLeadSource + description: >- + Retrieves lead source data for a customer with a specified ID. + + + A lead source is the marketing campaign that generates customer + interaction, a sale, or a trial. + + For more information, see [Lead source + attribution](https://www.rebilly.com/docs/dev-docs/concepts/#lead-source-attribution). + responses: + '200': + description: Lead source retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/LeadSource' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $customer = $client->customers()->load('myCustomerId'); + $leadSource = $customer->getLeadSource(); + - lang: JavaScript + source: >- + const lead = await api.customers.getLeadSource({id: + 'my-second-id'}); + + console.log(lead.fields.affiliate); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $customerLeadSource = + $service->customers()->getLeadSource('customerId'); + put: + x-products: + - Core + tags: + - Customers + summary: Create a customer's lead source + operationId: PutCustomerLeadSource + x-sdk-operation-name: createLeadSource + description: >- + Creates a lead source for a customer with a specified ID. + + + A lead source is the marketing campaign that + + generates customer interaction, a sale, or a trial. For more + + information, see + + [Lead source + attribution](https://www.rebilly.com/docs/dev-docs/concepts/#lead-source-attribution). + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/LeadSource' + description: Lead Source resource. + required: true + responses: + '201': + description: Lead source created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/LeadSource' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $leadSourceForm = new Rebilly\Entities\LeadSource(); + $leadSourceForm->setSource('TestSource'); + $leadSourceForm->setCampaign('TestCampaign'); + + try { + $customer = $client->customers()->updateLeadSource('myCustomerId', $leadSourceForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the properties for the new lead source + + const data = { + medium: 'foobar', + source: 'www.google.com', + campaign: 'my-first-campaign', + term: 'subscriptions', + content: 'subscription business', + affiliate: 'Acme', + subAffiliate: null, + salesAgent: null, + clickId: null, + path: null, + ipAddress: '12.34.56.78', + currency: 'USD', + amount: 0 + }; + + + const lead = await api.customers.createLeadSource({id: + 'my-second-id', data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $leadSourceForm = \Rebilly\Sdk\Model\LeadSource::from([]) + ->setSource('TestSource') + ->setCampaign('TestCampaign'); + + try { + $customer = $service->customers()->createLeadSource('myCustomerId', $leadSourceForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Core + tags: + - Customers + summary: Delete a customer's lead source + operationId: DeleteCustomerLeadSource + x-sdk-operation-name: deleteLeadSource + description: Deletes lead source data for a customer with a specified ID. + responses: + '204': + description: Lead source deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->customers()->deleteLeadSource('myCustomerId'); + - lang: JavaScript + source: >- + const request = await api.customers.deleteLeadSource({id: + 'my-second-id'}); + + + // the request does not return any fields but + + // you can confirm the success using the status code + + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $service->customers()->deleteLeadSource('customerId'); + '/customers/{id}/timeline': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Customers timeline + summary: Retrieve customer timeline messages + operationId: GetCustomerTimelineCollection + x-sdk-operation-name: getAllTimelineMessages + description: Retrieves customer timeline messages. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of customer timeline messages retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CustomerTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional except for the `id` + + const firstCollection = await api.customers + .getAllTimelineMessages({id: 'my-customer'}); + + // alternatively you can specify one or more of them + + const params = {id: 'my-customer', limit: 20, offset: 100}; + + const secondCollection = await + api.customers.getAllTimelineMessages(params); + + + // access the collection items, each item is a Member + + secondCollection.items + .forEach(message => console.log(message.fields.eventType)); + post: + x-products: + - Core + tags: + - Customers timeline + summary: Create a customer timeline comment + operationId: PostCustomerTimeline + x-sdk-operation-name: createTimelineComment + description: Creates a customer timeline comment or custom defined event. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerTimeline' + description: Customer timeline resource. + required: true + responses: + '201': + description: Customer timeline comment or custom defined event created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // Create a comment + + const firstComment = await api + .customers.createTimelineComment({id: 'my-customer-id', data: {message: 'Your comment here'}}); + + // Using params object, mentions and references + + const message = `Example of mentions @user@mydomain.com and + references #customers-customer-id`; + + const params = { + id: 'my-customer-id', + data: { + message, + }, + }; + + const secondComment = await + api.customers.createTimelineComment(params); + '/customers/{id}/timeline/{messageId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: messageId + in: path + description: ID of the customer timeline message. + required: true + schema: + type: string + get: + x-products: + - Core + tags: + - Customers timeline + summary: Retrieve a customer timeline message + operationId: GetCustomerTimeline + x-sdk-operation-name: getTimelineMessage + description: Retrieves a customer message with a specified ID. + responses: + '200': + description: Customer message retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const message = await api.customers + .getTimelineMessage({id: 'foobar-001', messageId: 'message-202'}); + console.log(message.fields.eventType); + delete: + x-products: + - Core + tags: + - Customers timeline + summary: Delete a customer timeline message + operationId: DeleteCustomerTimeline + x-sdk-operation-name: deleteTimelineMessage + description: Deletes a customer timeline message with a specified ID. + responses: + '204': + description: Customer timeline message deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: JavaScript + source: |- + const request = await api.customers + .deleteTimelineMessage({id: 'foobar-001', messageId: 'message-202'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + /disputes: + get: + x-products: + - Core + tags: + - Disputes + summary: Retrieve disputes + operationId: GetDisputeCollection + x-sdk-operation-name: getAll + description: Retrieves a list of all disputes. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: List of disputes successfully retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Dispute' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $disputes = $client->disputes()->search([ + 'filter' => 'transactionId:testId', + ]); + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await api.disputes.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.disputes.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(dispute => + console.log(dispute.fields.transactionId)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $disputesPaginator = $service->disputes()->getAllPaginator(filter: + 'transactionId:testId', limit: 5); + + foreach ($disputesPaginator as $disputesPage) { + printf("Disputes page %d/%d\n", $disputesPaginator->key() + 1, $disputesPaginator->count()); + foreach ($disputesPage as $dispute) { + printf("Dispute #%s (%s)\n", $dispute->getId(), $dispute->getStatus()); + } + } + + + // OR + + + $disputes = $service->disputes()->getAll(filter: + 'transactionId:testId'); + + foreach ($disputes as $dispute) { + printf("Dispute #%s (%s)\n", $dispute->getId(), $dispute->getStatus()); + } + post: + x-products: + - Core + tags: + - Disputes + summary: Create a dispute + operationId: PostDispute + x-sdk-operation-name: create + description: Creates a new dispute. + requestBody: + $ref: '#/components/requestBodies/Dispute' + responses: + '201': + description: Dispute created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Dispute' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $disputeForm = new Rebilly\Entities\Dispute(); + $disputeForm->setTransactionId('transactionId'); + $disputeForm->setCurrency('USD'); + $disputeForm->setAmount(10); + $disputeForm->setReasonCode(1000); + $disputeForm->setType($disputeForm::TYPE_1CB); + $disputeForm->setStatus($disputeForm::STATUS_RESPONSE_NEEDED); + $disputeForm->setPostedTime('2025-01-01 05:00:00'); + + try { + $dispute = $client->disputes()->create($disputeForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the properties for the new dispute + + const data = { + transactionId: 'my-transaction-id', + currency: 'USD', + amount: 5, + reasonCode: '1000', + type: 'first-chargeback', + status: 'response-needed', + acquirerReferenceNumber: '143543', + postedTime: '2017-09-19T20:46:48Z', + deadlineTime: '2017-09-19T20:46:48Z' + }; + + + // the ID is optional + + const firstdispute = await api.disputes.create({data}); + + + // or you can provide one + + const secondDispute = await api.disputes.create({id: 'my-second-id', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $disputeForm = new \Rebilly\Sdk\Model\Dispute(); + $disputeForm->setTransactionId('transactionId'); + $disputeForm->setCurrency('USD'); + $disputeForm->setAmount(10); + $disputeForm->setReasonCode('1000'); + $disputeForm->setType($disputeForm::TYPE_INFORMATION_REQUEST); + $disputeForm->setStatus($disputeForm::STATUS_RESPONSE_NEEDED); + $disputeForm->setPostedTime('2025-01-01 05:00:00'); + + try { + $dispute = $service->disputes()->create($disputeForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/disputes/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Disputes + summary: Retrieve a dispute + operationId: GetDispute + x-sdk-operation-name: get + description: Retrieves a dispute with a specified ID. + responses: + '200': + description: Dispute retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Dispute' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $dispute = $client->disputes()->load('disputeId'); + - lang: JavaScript + source: |- + const dispute = await api.disputes.get({id: 'foobar-001'}); + console.log(dispute.fields.firstName); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $dispute = $service->disputes()->get('disputeId'); + put: + x-products: + - Core + tags: + - Disputes + summary: Upsert a dispute + operationId: PutDispute + x-sdk-operation-name: update + description: Creates or updates (upserts) a dispute with a specified ID. + requestBody: + $ref: '#/components/requestBodies/Dispute' + responses: + '200': + description: Dispute updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Dispute' + '201': + description: Dispute created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Dispute' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $disputeForm = new Rebilly\Entities\Dispute(); + $disputeForm->setTransactionId('transactionId'); + $disputeForm->setCurrency('USD'); + $disputeForm->setAmount(10); + $disputeForm->setReasonCode(1000); + $disputeForm->setType($disputeForm::TYPE_1CB); + $disputeForm->setStatus($disputeForm::STATUS_RESPONSE_NEEDED); + $disputeForm->setPostedTime('2025-01-01 05:00:00'); + + try { + $dispute = $client->disputes()->update('disputeId', $dispute); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // creating a dispute + + const data = { + transactionId: 'my-transaction-id', + currency: 'USD', + amount: 5, + reasonCode: '1000', + type: 'first-chargeback', + status: 'response-needed', + acquirerReferenceNumber: '143543', + postedTime: '2017-09-19T20:46:48Z', + deadlineTime: '2017-09-19T20:46:48Z' + }; + + + // the ID is optional + + const firstdispute = await api.disputes.create({data}); + + + // or you can provide one + + const secondDispute = await api.disputes.create({id: 'my-second-id', + data}); + + + + + // updating a dispute + + const data = { + transactionId: 'my-other-transaction-id', + currency: 'USD', + amount: 5, + reasonCode: '1000', + type: 'first-chargeback', + status: 'response-needed', + acquirerReferenceNumber: '143543', + postedTime: '2017-09-19T20:46:48Z', + deadlineTime: '2017-09-19T20:46:48Z' + }; + + + const dispute = await api.disputes.update({id: 'my-second-id', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $disputeForm = new \Rebilly\Sdk\Model\Dispute(); + $disputeForm->setTransactionId('transactionId'); + $disputeForm->setCurrency('USD'); + $disputeForm->setAmount(10); + $disputeForm->setReasonCode('1000'); + $disputeForm->setType($disputeForm::TYPE_INFORMATION_REQUEST); + $disputeForm->setStatus($disputeForm::STATUS_RESPONSE_NEEDED); + $disputeForm->setPostedTime('2025-01-01 05:00:00'); + + try { + $dispute = $service->disputes()->update('disputeId', $disputeForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + /external-identifiers: + get: + x-products: + - Users + tags: + - External identifiers + summary: Retrieve external identifiers + operationId: GetExternalIdentifierCollection + x-sdk-operation-name: getAll + description: Retrieves a list of external identifiers. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: List of external identifiers retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ExternalIdentifier' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/{resource}/{resourceId}/external-identifiers/{service}': + parameters: + - $ref: '#/components/parameters/externalIdentifierResource' + - $ref: '#/components/parameters/externalIdentifierResourceId' + - $ref: '#/components/parameters/externalIdentifierService' + get: + x-products: + - Users + tags: + - External identifiers + summary: Retrieve an external identifier + operationId: GetExternalIdentifier + x-sdk-operation-name: get + description: >- + Retrieves an external identifier with a specified resource, resource ID + and, service name. + responses: + '200': + description: External identifier retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalIdentifier' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + post: + x-products: + - Users + tags: + - External identifiers + summary: Sync an external identifier + operationId: PostExternalIdentifier + x-sdk-operation-name: sync + description: >- + Synchronizes an external identifier with a specified resource, resource + ID, and service name. + responses: + '204': + description: External identifier sync scheduled. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + put: + x-products: + - Users + tags: + - External identifiers + summary: Upsert an external identifier + operationId: PutExternalIdentifier + x-sdk-operation-name: update + description: >- + Creates or updates an external identifier with a specified resource, + resource ID, and service name. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalIdentifier' + required: true + responses: + '200': + description: External identifier updated. + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalIdentifier' + '201': + description: External identifier created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalIdentifier' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Users + tags: + - External identifiers + summary: Delete an external identifier + operationId: DeleteExternalIdentifier + x-sdk-operation-name: delete + description: >- + Deletes an external identifier with a specified resource, resource ID, + and service name. + responses: + '204': + description: External identifier deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + /external-services-settings: + get: + x-products: + - Users + tags: + - External identifiers + summary: Retrieve external service settings + operationId: GetExternalServiceSettings + x-sdk-operation-name: getExternalServiceSettings + description: Retrieves external service settings. + responses: + '200': + description: External service settings. + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalServiceSettings' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + put: + x-products: + - Users + tags: + - External identifiers + summary: Update external service settings + operationId: PutExternalServiceSettings + x-sdk-operation-name: updateExternalServiceSettings + description: Updates the external service settings. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalServiceSettings' + required: true + responses: + '200': + description: External service settings updated. + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalServiceSettings' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /files: + get: + x-products: + - Core + tags: + - Files + summary: Retrieve files + operationId: GetFileCollection + x-sdk-operation-name: getAll + description: Retrieves a list of files. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionFields' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of files retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/File' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $files = $client->files()->search([ + 'filter' => 'name:TestFile', + ]); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.files.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.files.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(file => + console.log(file.fields.name)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $filesPaginator = $service->files()->getAllPaginator(limit: 5, + filter: 'name:TestFile'); + + foreach ($filesPaginator as $filesPage) { + printf("Files page %d/%d\n", $filesPaginator->key() + 1, $filesPaginator->count()); + foreach ($filesPage as $file) { + printf("File #%s: %s\n", $file->getId(), $file->getName()); + } + } + + + // OR + + + $files = $service->files()->getAll(filter: 'name:TestFile'); + + foreach ($files as $file) { + printf("File #%s: %s\n", $file->getId(), $file->getName()); + } + post: + x-products: + - Core + tags: + - Files + summary: Create a file + operationId: PostFile + x-sdk-operation-name: upload + security: + - SecretApiKey: [] + - JWT: [] + - PublishableApiKey: [] + description: |- + Creates a file. + + Additionally, a file can be sent by: + + - Multipart/form-data POST request: In this request, the file is + uploaded and all property names are the same as the JSON names. + - File body request: In this request, the file body is sent as + the request body, with the appropriate `Content-Type`. No + additional properties can be set with the request data. + + Permitted file types: `.jpg`, `.png`, `.gif`, and `.pdf`. + + When using a publishable API key, only private files can be created. + The files can be modified at a later point or time, or can be + accessed using a secret API key. + requestBody: + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/FileCreateFromInline' + - $ref: '#/components/schemas/FileCreateFromUrl' + required: true + responses: + '201': + description: File created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/File' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $fileForm = new Rebilly\Entities\File(); + $fileForm->setUrl('http://test.com/somefile.jpg'); + + try { + $file = $client->files()->create($fileForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // using a FileList to fetch a file + + const fileObject = fileList[0]; + + + const addedFile = await api.files.upload({fileObject}); + + + + + + // or, upload and update a file at the same time + + + // using a FileList to fetch a file + + const fileObject = fileList[0]; + + + // define file data + + const data = { + description: 'my new file', + tags: ['original'] + }; + + + const addedFile = await api.files.uploadAndUpdate({fileObject, + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $fileForm = new \Rebilly\Sdk\Model\FileCreateFromUrl(); + $fileForm->setUrl('http://test.com/somefile.jpg'); + + try { + $file = $service->files()->upload($fileForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/files/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Files + summary: Retrieve a file record + operationId: GetFile + x-sdk-operation-name: get + description: Retrieves a file with a specified ID. + responses: + '200': + description: File retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/File' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $file = $client->files()->load('fileId'); + - lang: JavaScript + source: |- + const file = await api.files.get({id: 'foobar-001'}); + console.log(file.fields.name); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $file = $service->files()->get('fileId'); + put: + x-products: + - Core + tags: + - Files + summary: Update a file + operationId: PutFile + x-sdk-operation-name: update + description: |- + Updates a file with a specified ID. + + > **Note:** Files can only be uploaded by POST request. + For more information, see the Create a file operation. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/File' + description: File resource. + required: true + responses: + '200': + description: File updated. + content: + application/json: + schema: + $ref: '#/components/schemas/File' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $fileForm = new Rebilly\Entities\File(); + $fileForm->setDescription('This is a test file'); + + try { + $file = $client->files()->update('fileId', $fileForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // define the values to update + + const data = { + name: 'new file name', + description: 'a fitting description', + tags: ['original'] + }; + + + const file = await api.files.update({id: 'my-file-id', data}); + + + + + + // or, upload and update a file at the same time + + + // using a FileList to fetch a file + + const fileObject = fileList[0]; + + + // define file data + + const data = { + description: 'my new file', + tags: ['original'] + }; + + + const addedFile = await api.files.uploadAndUpdate({fileObject, + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $fileForm = new \Rebilly\Sdk\Model\File(); + $fileForm->setDescription('This is a test file'); + + try { + $file = $service->files()->update('fileId', $fileForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Core + tags: + - Files + summary: Delete a file + operationId: DeleteFile + x-sdk-operation-name: delete + description: Deletes a file with a specified ID. + responses: + '204': + description: File deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->files()->delete('fileId'); + - lang: JavaScript + source: >- + // simply delete a file + + const request = await api.files.delete({id: 'my-file-id'}); + + + // the request does not return any fields but + + // you can confirm the success using the status code + + console.log(request.response.status); // 204 + + + + + + // or, delete a file and its related resource attachments + + // use this method to remove the file completely from all resources + at once. + + + const request = await api.files.detachAndDelete({id: 'my-file-id'}); + + + // the request does not return any fields but + + // you can confirm the success using the status code + + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $service->files()->delete('fileId'); + '/files/{id}/download': + parameters: + - $ref: '#/components/parameters/resourceId' + - $ref: '#/components/parameters/imageSize' + get: + x-products: + - Core + tags: + - Files + summary: Download a file + operationId: GetFileDownload + x-sdk-operation-name: download + description: Downloads a file with a specified ID. + responses: + '200': + description: File retrieved. + headers: + Content-Length: + description: Number of bytes in the file. + schema: + type: integer + example: 48 + Content-Type: + description: MIME type of the file. + schema: + type: string + example: image/png + content: + application/json: + schema: + type: string + readOnly: true + format: binary + application/pdf: + schema: + type: string + readOnly: true + format: binary + application/zip: + schema: + type: string + readOnly: true + format: binary + image/jpg: + schema: + type: string + readOnly: true + format: binary + image/png: + schema: + type: string + readOnly: true + format: binary + image/gif: + schema: + type: string + readOnly: true + format: binary + text/csv: + schema: + type: string + readOnly: true + format: binary + '302': + $ref: '#/components/responses/Found' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const file = await api.files.download({id: 'my-file-id'}); + + // access the file ArrayBuffer to view the content + console.log(file.data); + '/application-instances/{applicationId}': + parameters: + - $ref: '#/components/parameters/applicationId' + get: + x-products: + - Users + tags: + - Application users + summary: Retrieve an application instance + operationId: GetApplicationInstance + x-sdk-operation-name: get + description: |- + Retrieves a list of application instances. + You may sort applications by: `id`, `name`, `status`, `createdTime`, + and `updatedTime`. + responses: + '200': + description: Application instance retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationInstance' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $applicationInstance = + $client->applicationInstances()->load('applicationId'); + - lang: JavaScript + source: > + const application = await + api.applicationInstances.get({applicationId: 'applicationId'}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + $applicationInstance = + $service->applicationInstances()->get('applicationId'); + put: + x-products: + - Users + tags: + - Application users + summary: Upsert an application instance + operationId: PutApplicationInstance + x-sdk-operation-name: upsert + description: Creates or updates (upserts) an application instance. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationInstance' + responses: + '200': + description: Application instance updated. + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationInstance' + '201': + description: Application instance created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationInstance' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $applicationInstance = new Rebilly\Entities\ApplicationInstance(); + $applicationInstance->setSettings(['color' => 'red', 'limit' => 5]); + + try { + $applicationInstance = $client->applicationInstances()->update('applicationId', $applicationInstance); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // define values to upsert + + const data = { + settings: { + color: 'red', + limit: 5, + }, + }; + + + const apiKey = await api.applicationInstances.upsert({applicationId: + 'applicationId', data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + $applicationInstance = + \Rebilly\Sdk\Model\ApplicationInstance::from([ + 'settings' => ['color' => 'red', 'limit' => 5], + ]); + + + try { + $applicationInstance = $service->applicationInstances()->upsert('applicationId', $applicationInstance); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Users + tags: + - Application users + summary: Delete application instance + operationId: DeleteApplicationInstance + x-sdk-operation-name: delete + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Deletes an application instance with a specific ID. + responses: + '204': + description: Application instance deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->applicationInstances()->delete('applicationId'); + - lang: JavaScript + source: > + const request = await + api.applicationInstances.delete({applicationId: 'applicationId'}); + + + // the request does not return any fields but + + // you can confirm the success using the status code + + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $service->applicationInstances()->delete('applicationId'); + '/application-instances/{applicationId}/configuration': + parameters: + - $ref: '#/components/parameters/applicationId' + get: + x-products: + - Users + tags: + - Application users + summary: Retrieve an application instance configuration + operationId: GetApplicationInstanceConfiguration + x-sdk-operation-name: getConfiguration + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: >- + Retrieves the configuration of an installed application with a specified + ID. + responses: + '200': + description: Application instance configuration retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationInstanceConfiguration' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + put: + x-products: + - Users + tags: + - Application users + summary: Upsert an application instance configuration + operationId: PutApplicationInstanceConfiguration + x-sdk-operation-name: upsertConfiguration + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Creates or updates (upserts) an application instance configuration. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationInstanceConfiguration' + responses: + '200': + description: Application instance configuration updated. + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationInstanceConfiguration' + '201': + description: Application instance configuration created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationInstanceConfiguration' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /applications: + get: + x-products: + - Users + tags: + - Application users + summary: Retrieve applications + operationId: GetApplicationCollection + x-sdk-operation-name: getAll + description: |- + Retrieves a list of all applications. + You may sort applications by: `id`, `name`, `status`, `createdTime`, + and `updatedTime`. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionExpand' + - $ref: '#/components/parameters/collectionFields' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of applications retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UserApplication' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $applications = $client->applications()->search([ + 'filter' => 'status:available', + ]); + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await api.applications.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.applications.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(apiKey => + console.log(apiKey.applications.name)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + + $applicationsPaginator = + $service->applications()->getAllPaginator(filter: + 'status:available', limit: 5); + + foreach ($applicationsPaginator as $applicationPage) { + printf("Applications page %d/%d\n", $applicationsPaginator->key() + 1, $applicationsPaginator->count()); + foreach ($applicationPage as $application) { + printf("Application #%s (%s): %s\n", $application->getId(), $application->getStatus(), $application->getName()); + } + } + + + // OR + + + $applications = $service->applications()->getAll(filter: + 'status:available'); + + foreach ($applications as $application) { + printf("Application #%s (%s): %s\n", $application->getId(), $application->getStatus(), $application->getName()); + } + post: + x-products: + - Users + tags: + - Application owners + summary: Register an application + operationId: PostApplication + x-sdk-operation-name: create + description: >- + Registers an application and displays it publicly in the [App + Store](https://app.rebilly.com/app-store). + + Before you submit an application, read the [App Store + documentation](https://www.rebilly.com/docs/content/concepts-and-features/app-store/). + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Application' + responses: + '201': + description: Application registered. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Application' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $application = new Rebilly\Entities\Application(); + $application->setName('My application'); + $application->setDescription('My application description'); + $application->setLogoId('fileId'); + $application->setPermissions(['GetTransactionCollection']); + + try { + $application = $client->applications()->create($application); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: | + // first set the properties for the new application + const data = { + name: 'My application', + description: 'My application description', + logoId: 'fileId', + permissions: ['GetTransactionCollection'], + }; + + const application = await api.applications.create({data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $application = new \Rebilly\Sdk\Model\Application(); + $application->setName('My application'); + $application->setDescription('My application description'); + $application->setLogoId('fileId'); + $application->setPermissions(['GetTransactionCollection']); + + try { + $application = $service->applications()->create($application); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/applications/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Application users + summary: Retrieve application + operationId: GetApplication + x-sdk-operation-name: get + description: Retrieves an application with a specified ID. + responses: + '200': + description: Applications retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/UserApplication' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $application = $client->applications()->load('applicationId'); + - lang: JavaScript + source: > + const application = await api.applications.get({id: + 'applicationId'}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $application = $service->applications()->get('applicationId'); + '/applications/{id}/instances': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Application owners + summary: Retrieve application instances + operationId: GetApplicationInstanceCollection + x-sdk-operation-name: getInstances + description: Retrieves application instances with specified IDs. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Application instances retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/OwnerApplicationInstance' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $applicationInstances = + $client->applications()->loadInstances('applicationId'); + - lang: JavaScript + source: > + const applicationInstances = await + api.applications.getInstances({id: 'applicationId'}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + $applicationInstances = + $service->applicationInstances()->get('applicationId'); + '/applications/{id}/instances/{organizationId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - $ref: '#/components/parameters/organizationId' + get: + x-products: + - Users + tags: + - Application owners + summary: Retrieve an application instance + operationId: GetApplicationInstanceByOrganization + x-sdk-operation-name: getInstance + description: >- + Retrieves an application instance with specified application and + organization IDs. + responses: + '200': + description: Application instance retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/OwnerApplicationInstance' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $applicationInstance = + $client->applications()->loadInstance('applicationId', + 'organizationId'); + - lang: JavaScript + source: > + const applicationInstance = await api.applications.getInstance({id: + 'applicationId', organizationId: 'organizationId'}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + $applicationInstance = + $service->applications()->getInstance('applicationId', + 'organizationId'); + /invoices: + get: + x-products: + - Core + tags: + - Invoices + summary: Retrieve invoices + operationId: GetInvoiceCollection + x-sdk-operation-name: getAll + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a list of invoices. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: List of invoices retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $invoices = $client->invoices()->search([ + 'filter' => 'customerId:testCustomerId', + ]); + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await api.invoices.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.invoices.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(invoice => + console.log(invoice.fields.firstName)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $invoicesPaginator = $service->invoices()->getAllPaginator(filter: + 'customerId:testCustomerId', limit: 5); + + foreach ($invoicesPaginator as $invoicePage) { + printf("Invoices page %d/%d\n", $invoicesPaginator->key() + 1, $invoicesPaginator->count()); + foreach ($invoicePage as $invoice) { + printf("Invoice #%s (%s): %.2f\n", $invoice->getId(), $invoice->getStatus(), $invoice->getAmount()); + } + } + + + // OR + + + $invoices = $service->invoices()->getAll(filter: + 'customerId:testCustomerId', limit: 100); + + foreach ($invoices as $invoice) { + printf("Invoice #%s (%s): %.2f\n", $invoice->getId(), $invoice->getStatus(), $invoice->getAmount()); + } + post: + x-products: + - Core + tags: + - Invoices + summary: Create an invoice + operationId: PostInvoice + x-sdk-operation-name: create + description: Creates an invoice. + requestBody: + $ref: '#/components/requestBodies/Invoice' + responses: + '201': + description: Invoice created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $invoiceForm = new Rebilly\Entities\Invoice(); + $invoiceForm->setCustomerId('customerId'); + $invoiceForm->setWebsiteId('websiteId'); + $invoiceForm->setCurrency('USD'); + $invoiceForm->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + try { + $invoice = $client->invoices()->create($invoiceForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the properties for the new invoice + + const data = { + customerId: 'foobar-0001', + websiteId: 'my-main-website', + currency: 'USD', + billingAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }], + }, + deliveryAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }], + }, + notes: `customer's first invoice`, + }; + + + // the ID is optional + + const firstInvoice = await api.invoices.create({data}); + + + // or you can provide one + + const secondInvoice = await api.invoices.create({id: 'my-second-id', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $invoice = \Rebilly\Sdk\Model\Invoice::from() + ->setCustomerId('customerId') + ->setWebsiteId('websiteId') + ->setCurrency('USD') + ->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + try { + $invoice = $service->invoices()->create($invoice); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/invoices/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Invoices + summary: Retrieve an invoice + operationId: GetInvoice + x-sdk-operation-name: get + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves an invoice with a specified ID. + parameters: + - $ref: '#/components/parameters/mediaTypeJsonPdf' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Invoice retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + application/pdf: + schema: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $invoice = $client->invoices()->load('invoiceId'); + - lang: JavaScript + source: |- + const invoice = await api.invoices.get({id: 'foobar-001'}); + console.log(invoice.fields.primaryAddress.firstName); + + + // alternatively, download as a PDF file + const pdf = await api.invoices.downloadPDF({id: 'foobar-001'}); + // the invoice's data in arraybuffer format + console.log(pdf.data); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $invoice = $service->invoices()->get('invoiceId'); + put: + x-products: + - Core + tags: + - Invoices + summary: Upsert an invoice + operationId: PutInvoice + x-sdk-operation-name: update + description: Creates or updates (upserts) an invoice with a specified ID. + requestBody: + $ref: '#/components/requestBodies/Invoice' + responses: + '200': + description: Invoice updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '201': + description: Invoice created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $invoiceForm = new Rebilly\Entities\Invoice(); + $invoiceForm->setCustomerId('customerId'); + $invoiceForm->setWebsiteId('websiteId'); + $invoiceForm->setCurrency('USD'); + $invoiceForm->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + try { + $invoice = $client->invoices()->update('invoiceId', $invoiceForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // creating an invoice + + const data = { + customerId: 'foobar-0001', + websiteId: 'my-main-website', + currency: 'USD', + billingAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }], + }, + deliveryAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }], + }, + notes: `customer's first invoice`, + }; + + + // the ID is optional + + const firstInvoice = await api.invoices.create({data}); + + + // or you can provide one + + const secondInvoice = await api.invoices.create({id: 'my-second-id', + data}); + + + + + // updating an invoice + + const data = { + customerId: 'foobar-0001', + websiteId: 'my-main-website', + currency: 'USD', + billingAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }], + }, + deliveryAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }], + }, + notes: `customer's first invoice`, + }; + + + const invoice = await api.invoices.update({id: 'my-second-id', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $invoice = \Rebilly\Sdk\Model\Invoice::from() + ->setCustomerId('customerId') + ->setWebsiteId('websiteId') + ->setCurrency('USD') + ->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + try { + $invoice = $service->invoices()->update('invoiceId', $invoice); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/invoices/{id}/items': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Invoices + summary: Retrieve invoice items + operationId: GetInvoiceItemCollection + x-sdk-operation-name: getAllInvoiceItems + description: Retrieves invoice items with a specified invoice ID. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Invoice items retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/InvoiceItem' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $invoiceItems = $client->invoiceItems()->search('invoiceId', [ + 'filter' => 'quantity:5', + ]); + - lang: JavaScript + source: >- + // get the top 20 invoice items for this ID + + const invoiceItems = await api.invoices.getAllInvoiceItems({id: + 'my-invoice-id', limit: 20}); + + invoiceItems.items.forEach(item => + console.log(item.fields.description)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $invoiceItems = + $service->invoices()->getAllInvoiceItems('invoiceId'); + post: + x-products: + - Core + tags: + - Invoices + summary: Create an invoice item + operationId: PostInvoiceItem + x-sdk-operation-name: createInvoiceItem + description: Creates an invoice item. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceItem' + description: InvoiceItem resource. + required: true + responses: + '201': + description: Invoice item created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceItem' + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $invoiceItemForm = new Rebilly\Entities\InvoiceItem(); + $invoiceItemForm->setType($invoiceItemForm::TYPE_DEBIT); + $invoiceItemForm->setUnitPrice(0.99); + $invoiceItemForm->setQuantity(5); + + try { + $invoiceItem = $client->invoiceItems()->create($invoiceItemForm, 'invoiceId'); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the properties for the new invoice item + + const data = { + type: 'debit', + unitPrice: 5 + }; + + + const invoiceItem = await api.invoices.createInvoiceItem({id: + 'my-second-id', data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $invoiceItem = new \Rebilly\Sdk\Model\InvoiceItem(); + + $invoiceItemForm->setType(\Rebilly\Sdk\Model\InvoiceItem::TYPE_DEBIT); + + $invoiceItemForm->setUnitPrice(0.99); + + $invoiceItemForm->setQuantity(5); + + + try { + $invoiceItem = $service->invoices()->createInvoiceItem('invoiceId', $invoiceItem); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/invoices/{id}/items/{itemId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: itemId + in: path + description: ID of the invoice item. + required: true + schema: + type: string + get: + x-products: + - Core + tags: + - Invoices + summary: Retrieve an invoice item + operationId: GetInvoiceItem + x-sdk-operation-name: getInvoiceItem + description: Retrieves an invoice item. + responses: + '200': + description: Invoice item retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceItem' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $invoiceItems = $client->invoiceItems()->load('invoiceId'); + - lang: JavaScript + source: > + const invoiceItem = await api.invoices.getInvoiceItem({id: + 'foobar-001'}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $invoiceItem = $service->invoices()->getInvoiceItem('invoiceId', + 'invoiceItemId'); + put: + x-products: + - Core + tags: + - Invoices + summary: Update an invoice item + operationId: PutInvoiceItem + x-sdk-operation-name: updateInvoiceItem + description: Updates an invoice item. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceItem' + description: Invoice item resource. + required: true + responses: + '200': + description: Invoice item updated. + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceItem' + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $invoiceItemForm = new Rebilly\Entities\InvoiceItem(); + $invoiceItemForm->setType($invoiceItemForm::TYPE_DEBIT); + $invoiceItemForm->setUnitPrice(0.99); + $invoiceItemForm->setQuantity(5); + + try { + $invoiceItem = $client->invoiceItems()->update($invoiceItemForm, 'invoiceId', 'invoiceItemId'); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // first set the properties for the new invoice item + + const data = { + type: 'debit', + unitPrice: 5 + }; + + + const invoiceItem = await api.invoices.updateInvoiceItem({id: 'id', + itemId: 'itemId', data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $invoiceItem = \Rebilly\Sdk\Model\InvoiceItem::from([ + 'type' => \Rebilly\Sdk\Model\InvoiceItem::TYPE_DEBIT, + 'unitPrice' => 0.99, + 'quantity' => 5, + ]); + + try { + $invoiceItem = $service->invoices()->updateInvoiceItem('invoiceId', 'invoiceItemId', $invoiceItem); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Core + tags: + - Invoices + summary: Delete an invoice item + operationId: DeleteInvoiceItem + x-sdk-operation-name: deleteInvoiceItem + description: Deletes an invoice item. + responses: + '204': + description: Invoice item deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->invoiceItems()->delete('invoiceId', 'invoiceItemId'); + - lang: JavaScript + source: > + const invoiceItem = await api.invoices.deleteInvoiceItem({id: 'id', + itemId: 'itemId'}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $service->invoices()->deleteInvoiceItem('invoiceId', + 'invoiceItemId'); + '/invoices/{id}/issue': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Invoices + summary: Issue an invoice + operationId: PostInvoiceIssuance + x-sdk-operation-name: issue + description: |- + Issues an invoice with a specified ID. + The invoice must be in `draft` status. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceIssue' + description: Invoice issue resource. + required: true + responses: + '201': + description: Invoice issued. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $invoice = $client->invoices()->issue('invoiceId', '2025-01-01 + 05:00:00'); + - lang: JavaScript + source: >- + // define the issued time + + const data = { + issuedTime: "2017-09-19T20:46:51Z" + }; + + + // issue the invoice without an issued time + + const firstInvoice = await api.invoices.issue({id: 'my-first-id'}); + + + // or include it + + const secondInvoice = await api.invoices.issue({id: 'my-second-id', + data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $invoice = $service->invoices()->issue('invoiceId', new + \Rebilly\Sdk\Model\InvoiceIssue([ + 'issuedTime' => '2025-01-01 05:00:00', + ])); + '/invoices/{id}/abandon': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Invoices + summary: Abandon an invoice + operationId: PostInvoiceAbandonment + x-sdk-operation-name: abandon + description: Abandons an invoice with a specified ID. + responses: + '201': + description: Invoice abandoned. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $invoice = $client->invoices()->abandon('invoiceId'); + - lang: JavaScript + source: >- + const abandonedInvoice = await api.invoices.abandon({id: + 'my-invoice-id'}); + + console.log(abandonedInvoice.fields.status); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $invoice = $service->invoices()->abandon('invoiceId'); + '/invoices/{id}/void': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Invoices + summary: Void an invoice + operationId: PostInvoiceVoid + x-sdk-operation-name: void + description: Voids an invoice with a specified ID. + responses: + '201': + description: Invoice voided. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $invoice = $client->invoices()->void('invoiceId'); + - lang: JavaScript + source: >- + const voidedInvoice = await api.invoices.void({id: + 'my-invoice-id'}); + + console.log(voidedInvoice.fields.status); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $invoice = $service->invoices()->void('invoiceId'); + '/invoices/{id}/recalculate': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Invoices + summary: Recalculate an invoice + operationId: PostInvoiceRecalculation + x-sdk-operation-name: recalculate + description: >- + Recalculates shipping rates, taxes, and discounts on an invoice with a + specified ID. + + + One use case for this operation to apply a revoked coupon, + + or coupon that is redeemed by a customer after the invoice is issued. + responses: + '201': + description: Invoice recalculated. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $invoice = $service->invoices()->recalculate('invoiceId'); + '/invoices/{id}/reissue': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Invoices + summary: Reissue an invoice + operationId: PostInvoiceReissuance + x-sdk-operation-name: reissue + description: |- + Reissues an invoice with a specified ID. + The status of the invoice must be `unpaid` or `past-due`. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceReissue' + description: InvoiceReissue resource. + required: true + responses: + '201': + description: Invoice reissued. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $invoiceReissue = new \Rebilly\Sdk\Model\InvoiceReissue([ + 'dueTime' => '2025-01-01 05:00:00', + ]); + + try { + $invoiceReissue = $service->invoices()->reissue('invoiceId', $invoiceReissue); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/invoices/{id}/transaction-allocations': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Invoices + summary: Retrieve transaction amounts allocations + operationId: GetInvoiceTransactionAllocationCollection + x-sdk-operation-name: getAllTransactionAllocations + description: >- + Retrieves amounts from a transaction that are allocated as invoice + payments. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: List of allocations retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/InvoiceTransactionAllocation' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $transactionAllocations = + $service->invoices()->getAllTransactionAllocations('invoiceId'); + '/invoices/{id}/transaction': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Invoices + summary: Apply a transaction to an invoice + operationId: PostInvoiceTransaction + x-sdk-operation-name: applyTransaction + description: >- + Applies a transaction to an invoice. + + The invoice status must be `unpaid`, and the transaction must have a + non-zero unused amount. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceTransaction' + description: Invoice transaction resource. + required: true + responses: + '200': + description: Transaction applied to invoice. + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: | + const params = { + id: 'my-invoice-id', + transactionId: 'my-transaction-id', + amount: 12,99 + }; + + const invoice = await api.invoices.applyTransaction(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $invoiceTransaction = new \Rebilly\Sdk\Model\InvoiceTransaction([ + 'transactionId' => 'transactionId', + 'amount' => 149.99, + ]); + + try { + $invoiceTransaction = $service->invoices()->applyTransaction('invoiceId', $invoiceTransaction); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/invoices/{id}/timeline': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Invoices timeline + summary: Retrieve invoice timeline messages + operationId: GetInvoiceTimelineCollection + x-sdk-operation-name: getAllTimelineMessages + description: Retrieves a list of invoice timeline messages. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of invoice timeline messages retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/InvoiceTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional except for the `id` + + const firstCollection = await api.invoices + .getAllTimelineMessages({id: 'my-invoice'}); + + // alternatively you can specify one or more of them + + const params = {id: 'my-invoice', limit: 20, offset: 100}; + + const secondCollection = await + api.invoices.getAllTimelineMessages(params); + + + // access the collection items, each item is a Member + + secondCollection.items + .forEach(message => console.log(message.fields.eventType)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $timelineMessages = + $service->invoices()->getAllTimelineMessages('invoiceId', filter: + 'triggeredBy:direct-api'); + post: + x-products: + - Core + tags: + - Invoices timeline + summary: Create an invoice timeline comment + operationId: PostInvoiceTimeline + x-sdk-operation-name: createTimelineComment + description: Creates an invoice timeline comment. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceTimeline' + description: Invoice Timeline resource. + required: true + responses: + '201': + description: Invoice timeline comment created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // Create a comment + + const firstComment = await api + .invoices.createTimelineComment({id: 'my-invoice-id', data: {message: 'Your comment here'}}); + + // Using params object, mentions and references + + const message = `Example of mentions @user@mydomain.com and + references #invoice-subscription-id`; + + const params = { + id: 'my-invoice-id', + data: { + message, + }, + }; + + const secondComment = await + api.invoices.createTimelineComment(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $timelineMessage = new \Rebilly\Sdk\Model\InvoiceTimeline([ + 'message' => 'Automatic message', + ]); + + try { + $timelineMessage = $service->invoices()->createTimelineComment('invoiceId', $timelineMessage); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/invoices/{id}/timeline/{messageId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: messageId + in: path + description: ID of the invoice timeline message. + required: true + schema: + type: string + get: + x-products: + - Core + tags: + - Invoices timeline + summary: Retrieve an invoice timeline message + operationId: GetInvoiceTimeline + x-sdk-operation-name: getTimelineMessage + description: Retrieves an invoice timeline message with a specified ID. + responses: + '200': + description: Invoice message retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const message = await api.invoices + .getTimelineMessage({id: 'foobar-001', messageId: 'message-202'}); + console.log(message.fields.eventType); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $timelineMessage = + $service->invoices()->getTimelineMessage('invoiceId', 'messageId'); + delete: + x-products: + - Core + tags: + - Invoices timeline + summary: Delete an invoice timeline message + operationId: DeleteInvoiceTimeline + x-sdk-operation-name: deleteTimelineMessage + description: Deletes an invoice timeline message with a specified ID. + responses: + '204': + description: Invoice timeline message deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: JavaScript + source: |- + const request = await api.invoices + .deleteTimelineMessage({id: 'foobar-001', messageId: 'message-202'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $service->invoices()->deleteTimelineMessage('invoiceId', + 'messageId'); + /credit-memos: + get: + x-products: + - Core + tags: + - Credit memos + summary: Retrieve credit memos + operationId: GetCreditMemoCollection + x-sdk-operation-name: getAll + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a list of credit memos. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of credit memos retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CreditMemo' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Credit memos + summary: Create a credit memo + operationId: PostCreditMemo + x-sdk-operation-name: create + description: Creates a credit memo. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreditMemo' + responses: + '201': + description: Credit memo created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CreditMemo' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/credit-memos/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Credit memos + summary: Retrieve a credit memo + operationId: GetCreditMemo + x-sdk-operation-name: get + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a credit memo with a specified ID. + responses: + '200': + description: Credit memo retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/CreditMemo' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + tags: + - Credit memos + summary: Upsert a credit memo + operationId: PutCreditMemo + x-sdk-operation-name: update + description: Creates or updates (upserts) a credit memo with a specified ID. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreditMemo' + responses: + '200': + description: Credit memo updated. + content: + application/json: + schema: + $ref: '#/components/schemas/CreditMemo' + '201': + description: Credit memo created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CreditMemo' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + patch: + x-products: + - Core + tags: + - Credit memos + summary: Partially update a credit memo + operationId: PatchCreditMemo + x-sdk-operation-name: patch + description: Partially updates a credit memo with a specified ID. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PatchCreditMemo' + responses: + '200': + description: Credit memo updated. + content: + application/json: + schema: + $ref: '#/components/schemas/CreditMemo' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + '/credit-memos/{id}/void': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Credit memos + summary: Void a credit memo + operationId: PostCreditMemoVoid + x-sdk-operation-name: void + description: Voids a credit memo with a specified ID. + responses: + '201': + description: Credit memo voided. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CreditMemo' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/credit-memos/{id}/timeline': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Credit memos timeline + summary: Retrieve credit memo timeline messages + operationId: GetCreditMemoTimelineCollection + x-sdk-operation-name: getAllTimelineMessages + description: Retrieves a list of credit memo timeline messages. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of credit memo timeline messages retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CreditMemoTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Credit memos timeline + summary: Create a credit memo timeline message + operationId: PostCreditMemoTimeline + x-sdk-operation-name: createTimelineComment + description: Creates a credit memo timeline message. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreditMemoTimeline' + description: Credit memo timeline resource. + required: true + responses: + '201': + description: Credit memo timeline message created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CreditMemoTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/credit-memos/{id}/timeline/{messageId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: messageId + in: path + description: ID of the credit memo timeline message. + required: true + schema: + type: string + get: + x-products: + - Core + tags: + - Credit memos timeline + summary: Retrieve a credit memo timeline message + operationId: GetCreditMemoTimeline + x-sdk-operation-name: getTimelineMessage + description: Retrieves a credit memo timeline message with a specified ID. + responses: + '200': + description: Credit memo timeline message retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/CreditMemoTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Core + tags: + - Credit memos timeline + summary: Delete a credit memo timeline message + operationId: DeleteCreditMemoTimeline + x-sdk-operation-name: deleteTimelineMessage + description: Deletes a credit memo timeline message with a specified ID. + responses: + '204': + description: Credit memo timeline message deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + /journal-accounts: + get: + x-products: + - Core + tags: + - Journal + summary: Retrieve journal accounts + operationId: GetJournalAccountCollection + x-sdk-operation-name: getAllAccounts + description: Retrieves a list of journal accounts. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of journal accounts retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/JournalAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Journal + summary: Create a journal account + operationId: PostJournalAccount + x-sdk-operation-name: createAccount + description: Creates a journal account. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/JournalAccount' + description: Journal account resource. + required: true + responses: + '201': + description: Journal account created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/JournalAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/journal-accounts/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Journal + summary: Retrieve journal account + operationId: GetJournalAccount + x-sdk-operation-name: getAccount + description: Retrieves a journal account with a specified ID. + responses: + '200': + description: Journal account retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/JournalAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + tags: + - Journal + summary: Upsert a journal account + operationId: PutJournalAccount + x-sdk-operation-name: updateAccount + description: Creates or updates (upsert) a journal account. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/JournalAccount' + description: Journal account resource. + required: true + responses: + '200': + description: Journal account updated. + content: + application/json: + schema: + $ref: '#/components/schemas/JournalAccount' + '201': + description: Journal account created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/JournalAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + /journal-entries: + get: + x-products: + - Core + tags: + - Journal + summary: Retrieve journal entries + operationId: GetJournalEntryCollection + x-sdk-operation-name: getAllEntries + description: Retrieves a list of journal entries. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of journal entries retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/JournalEntry' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Journal + summary: Create a journal entry + operationId: PostJournalEntry + x-sdk-operation-name: createEntry + description: Creates a journal entry. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/JournalEntry' + description: Journal entry resource. + required: true + responses: + '201': + description: Journal entry created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/JournalEntry' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/journal-entries/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Journal + summary: Retrieve journal entry + operationId: GetJournalEntry + x-sdk-operation-name: getEntry + description: Retrieves a journal entry with a specified ID. + responses: + '200': + description: Journal entry retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/JournalEntry' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + tags: + - Journal + summary: Upsert a journal entry + operationId: PutJournalEntry + x-sdk-operation-name: updateEntry + description: Creates or updates (upsert) a journal entry. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/JournalEntry' + description: Journal entry resource. + required: true + responses: + '200': + description: Journal entry updated. + content: + application/json: + schema: + $ref: '#/components/schemas/JournalEntry' + '201': + description: Journal entry created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/JournalEntry' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + '/journal-entries/{id}/records': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Journal + summary: Retrieve journal records + operationId: GetJournalRecordCollection + x-sdk-operation-name: getAllRecords + description: Retrieves a list of journal records. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: List of journal records retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/JournalRecord' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Journal + summary: Create a journal record + operationId: PostJournalRecord + x-sdk-operation-name: createRecord + description: Creates a journal record. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/JournalRecord' + description: Journal record resource. + required: true + responses: + '201': + description: Journal record created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/JournalRecord' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/journal-entries/{id}/records/{journalRecordId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - $ref: '#/components/parameters/journalRecordId' + get: + x-products: + - Core + tags: + - Journal + summary: Retrieve journal record + operationId: GetJournalRecord + x-sdk-operation-name: getRecord + description: Retrieves a journal record with a specified ID. + responses: + '200': + description: Journal record retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/JournalRecord' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + tags: + - Journal + summary: Upsert a journal record + operationId: PutJournalRecord + x-sdk-operation-name: updateRecord + description: Creates or updates (upsert) a journal record. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/JournalRecord' + description: Journal record resource. + required: true + responses: + '200': + description: Journal record updated. + content: + application/json: + schema: + $ref: '#/components/schemas/JournalRecord' + '201': + description: Journal record created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/JournalRecord' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Core + tags: + - Journal + summary: Delete a journal record + operationId: DeleteJournalRecord + x-sdk-operation-name: deleteRecord + description: Deletes a journal record. + responses: + '204': + description: Journal record deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + /kyc-requests: + post: + x-products: + - Core + tags: + - KYC documents + summary: Create a KYC request + operationId: PostKycRequest + x-sdk-operation-name: create + description: >- + Creates a KYC request. + + + The customer is redirected to the `kycGatherer` link. + + After the customer completes the KYC process, + + they are redirected back to the `redirectUrl` link. + + + Corresponding webhooks, such as: KYC document accepted and KYC document + rejected, + + are sent to the subscribers. + + + When the complete list of documents is received and accepted, + + the KYC request fulfilled webhook is sent to subscribers. + + + If a `credit-file-proof` request is successful, + + it returns a `decision` value of `single-source` or `dual-source`. + + The corresponding `identity-proof` and `address-proof` documents are not + requested. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/KycRequest' + examples: + credit-file-identity-address: + summary: Credit file with fallback + description: >- + In the sandbox environment, to mark a `credit-file-proof` KYC + document as `accepted`, + + pass the customer's first name to the `accept` field. + + If an incorrect first name is passed, the document is + rejected. + value: + customerId: '123_456789' + redirectUrl: 'https://example.com/return-here/' + documents: + - type: credit-file-proof + maxAttempts: 1 + - type: identity-proof + - type: address-proof + reason: registration + identity-with-face-proof: + summary: Identity with face proof + value: + customerId: '123_456789' + redirectUrl: 'https://example.com/return-here/' + documents: + - type: identity-proof + faceProofRequired: true + reason: registration + identity-and-address: + summary: Identity and address + value: + customerId: '123_456789' + redirectUrl: 'https://example.com/return-here/' + documents: + - type: identity-proof + - type: address-proof + reason: registration + description: KYC request resource. + required: true + responses: + '201': + description: KYC request created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/KycRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + get: + x-products: + - Core + tags: + - KYC documents + summary: Retrieve KYC requests + operationId: GetKycRequestCollection + x-sdk-operation-name: getAll + description: Retrieves a list of KYC requests. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of KYC requests retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/KycRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/kyc-requests/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - KYC documents + summary: Retrieve a KYC request + operationId: GetKycRequest + x-sdk-operation-name: get + description: Retrieves a KYC request with a specified ID. + responses: + '200': + description: KYC request retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/KycRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + patch: + x-products: + - Core + tags: + - KYC documents + summary: Update a KYC request + operationId: PatchKycRequest + x-sdk-operation-name: update + description: Updates a KYC request with a specified ID. + requestBody: + $ref: '#/components/requestBodies/PatchKycRequest' + description: KYC document resource. + required: true + responses: + '200': + description: KYC request updated. + content: + application/json: + schema: + $ref: '#/components/schemas/KycRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Core + tags: + - KYC documents + summary: Delete the KYC request + operationId: DeleteKycRequest + x-sdk-operation-name: delete + description: Deletes a KYC request with a specified ID. + responses: + '204': + description: KYC request deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /kyc-documents: + get: + x-products: + - Core + tags: + - KYC documents + summary: Retrieve KYC documents + operationId: GetKycDocumentCollection + x-sdk-operation-name: getAll + description: Retrieves a list of KYC documents. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: List of KYC documents retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/KycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.kycDocuments.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.kycDocuments.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(document => + console.log(document.fields.documentType)); + post: + x-products: + - Core + tags: + - KYC documents + summary: Create a KYC document + operationId: PostKycDocument + x-sdk-operation-name: create + description: Creates a KYC document. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocument' + description: Kyc document resource. + required: true + responses: + '201': + description: KYC document created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: |- + // All fields are required + const data = { + fieldId: '4f6cf35x-2c4y-483z-a0a9-158621f77a21', + customerId: '4f6cf35x-2c4y-483z-a0a9-158621f77a21', + documentType: 'identity-proof' + }; + + const firstKycDocument = await api.kycDocuments.create({data}); + '/kyc-documents/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - KYC documents + summary: Retrieve a KYC document + operationId: GetKycDocument + x-sdk-operation-name: get + description: Retrieves a KYC document with a specified ID. + responses: + '200': + description: KYC document retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const document = await api.kycDocuments.get({id: 'foobar-001'}); + console.log(document.fields.documentType); + put: + x-products: + - Core + tags: + - KYC documents + summary: Upsert a KYC document + operationId: PutKycDocument + x-sdk-operation-name: update + description: Creates or updates (upserts) a KYC document with a specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocument' + description: KYC document resource. + required: true + responses: + '200': + description: KYC document updated. + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocument' + '201': + description: KYC document created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // define the values to update + + const data = { + status: 'accepted' + }; + + + const document = await api.kycDocuments.update({id: 'my-second-id', + data}); + '/kyc-documents/{id}/acceptance': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - KYC documents + summary: Accept a KYC document + operationId: PostKycDocumentAcceptance + x-sdk-operation-name: accept + description: |- + Accepts a KYC document and changes the documents `status` to `accepted`. + The review time and reviewer information is also updated. + + > **Note:** Use this operation for manual overrides. + responses: + '201': + description: KYC document acceptance created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + const acceptedDocument = await api.kycDocuments.accept({id: + 'my-second-id'}); + + console.log(acceptedDocument.fields.status); + '/kyc-documents/{id}/matches': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - KYC documents + summary: Update KYC document matches + operationId: PostKycDocumentMatches + x-sdk-operation-name: matches + description: |- + Updates the document matches of a KYC document with a specified ID. + + > **Note:** Use this operation for manual overrides. + requestBody: + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/KycIdentityMatches' + - $ref: '#/components/schemas/KycAddressMatches' + description: Kyc document resource. + required: true + responses: + '204': + description: Document matches updated. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/kyc-documents/{id}/rejection': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - KYC documents + summary: Reject a KYC document + operationId: PostKycDocumentRejection + x-sdk-operation-name: reject + description: |- + Rejects a KYC document and changes the `status` to `rejected`. + The review time and reviewer information is also updated. + + > **Note:** Use this operation for manual overrides. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocumentRejection' + description: KYC document resource. + required: true + responses: + '201': + description: KYC document rejected. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + const data = { + type: 'document-expired', + message: 'Document is expired' + } + + const rejectedDocument = await api.kycDocuments.reject({id: + 'my-second-id', data}); + + console.log(rejectedDocument.fields.rejectionReason.type); + '/kyc-documents/{id}/review': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - KYC documents + summary: Review a KYC document + operationId: PostKycDocumentReview + x-sdk-operation-name: review + description: >- + Updates the `reviewTime` and `reviewerId` of a KYC document with a + specified ID. + responses: + '201': + description: KYC document reviewed. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + const reviewedDocument = await api.kycDocuments.review({id: + 'my-second-id'}); + + console.log(reviewedDocument.fields.status); + '/kyc-documents/{id}/start-review': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - KYC documents + summary: Start review of a KYC document + operationId: PostKycDocumentStartReview + x-sdk-operation-name: startReview + description: >- + Starts the review process of a KYC document with a specified ID. + + + This operation also sets the KYC document `reviewStartTime` to the + current date-time, + + and updates the review information. + responses: + '201': + description: KYC document review started. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + '/kyc-documents/{id}/stop-review': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - KYC documents + summary: Stop review of a KYC document + operationId: PostKycDocumentStopReview + x-sdk-operation-name: stopReview + description: >- + Stops the review of a KYC document with a specified ID. + + + This operation also sets the KYC document `reviewStartTime` and the + reviewer information to null. + + Use this operation when the reviewer must stop the review. + + For example, if the reviewer must take a break, or ends a shift. + responses: + '201': + description: KYC document review stopped. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/KycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + /kyc-settings: + get: + x-products: + - Core + tags: + - KYC documents + summary: Retrieve KYC settings + operationId: GetKycSettings + x-sdk-operation-name: getKycSettings + description: Retrieves KYC settings. + responses: + '200': + description: KYC settings retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/KycSettings' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + tags: + - KYC documents + summary: Update KYC settings + operationId: PutKycSettings + x-sdk-operation-name: updateKycSettings + description: Updates KYC settings. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/KycSettings' + description: Settings for KYC analysis. + required: true + responses: + '200': + description: KYC settings updated. + content: + application/json: + schema: + $ref: '#/components/schemas/KycSettings' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /password-tokens: + get: + x-products: + - Full + - Core + tags: + - Customer authentication + summary: Retrieve tokens + operationId: GetPasswordTokenCollection + x-sdk-operation-name: getAllResetPasswordTokens + description: Retrieves a list of reset password tokens. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: List of reset password tokens retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ResetPasswordToken' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await + api.customerAuthentication.getAllResetPasswordTokens(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100}; + + const secondCollection = await + api.customerAuthentication.getAllResetPasswordTokens(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(token => + console.log(token.fields.token)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $passwordTokens = + $service->customerAuthentication()->getAllResetPasswordTokens(); + post: + x-products: + - Full + - Core + tags: + - Customer authentication + summary: Create a reset password token + operationId: PostPasswordToken + x-sdk-operation-name: createResetPasswordToken + description: Creates a reset password token. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ResetPasswordToken' + description: ResetPasswordToken resource. + required: true + responses: + '201': + description: Reset password token created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ResetPasswordToken' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // first set the required properties for the new credential + + const data = { + username: 'foobar', + password: 'fuubar', + // the `credential` expects + // the customer credential's ID + credential: 'foobar-0001' + + // optionally you can define an `expiredTime` to + // limit the duration of the reset token + + //expiredTime: '2017-09-18T19:17:39Z' + }; + + + const resetToken = await + api.customerAuthentication.createResetPasswordToken({data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $resetPasswordTokenForm = new + \Rebilly\Sdk\Model\ResetPasswordToken(); + + $resetPasswordTokenForm->setUsername('username'); + + + try { + $tokens = $coreService->customerAuthentication()->createResetPasswordToken($resetPasswordTokenForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/password-tokens/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Customer authentication + summary: Retrieve a reset password token + operationId: GetPasswordToken + x-sdk-operation-name: getResetPasswordToken + description: Retrieves a reset password token with a specified ID. + responses: + '200': + description: Reset password token retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ResetPasswordToken' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + const token = await + api.customerAuthentication.getResetPasswordToken({id: + 'my-first-id'}); + + console.log(token.fields.credential); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $passwordToken = + $service->customerAuthentication()->getResetPasswordToken('tokenId'); + delete: + x-products: + - Core + tags: + - Customer authentication + summary: Delete a reset password token + operationId: DeletePasswordToken + x-sdk-operation-name: deleteResetPasswordToken + description: Deletes a reset password token with a specified ID. + responses: + '204': + description: Reset password token deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: JavaScript + source: >- + const request = await + api.customerAuthentication.deleteResetPasswordToken({id: + 'my-second-key'}); + + + // the request does not return any fields but + + // you can confirm the success using the status code + + console.log(request.response.status); // 204 + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $service->customerAuthentication()->deleteResetPasswordToken('tokenId'); + /payment-instruments: + get: + x-products: + - Core + tags: + - Payment instruments + summary: Retrieve payment instruments + operationId: GetPaymentInstrumentCollection + x-sdk-operation-name: getAll + description: Retrieves a list of payment instruments. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: List of payment instruments retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PaymentInstrument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $paymentInstruments = $client->paymentInstruments()->search([ + 'filter' => 'status:active;method:payment-card', + ]); + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await api.paymentInstruments.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await + api.paymentInstruments.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(paymentInstrument => + console.log(paymentInstrument.fields.customerId)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + + $paymentInstrumentsPaginator = + $service->paymentInstruments()->getAllPaginator( + filter: 'status:active;method:payment-card', + limit: 5 + ); + + foreach ($paymentInstrumentsPaginator as $paymentInstrumentsPage) { + printf("Payment instruments page %d/%d\n", $paymentInstrumentsPaginator->key() + 1, $paymentInstrumentsPaginator->count()); + foreach ($paymentInstrumentsPage as $paymentInstrument) { + printf("Payment instrument #%s\n", $paymentInstrument->getId()); + } + } + + + // OR + + + $paymentInstruments = $service->paymentInstruments()->getAll(filter: + 'status:active;method:payment-card'); + + foreach ($paymentInstruments as $paymentInstrument) { + printf("Payment instrument #%s\n", $paymentInstrument->getId()); + } + post: + x-products: + - Core + tags: + - Payment instruments + summary: Create a payment instrument + operationId: PostPaymentInstrument + x-sdk-operation-name: create + description: >- + Creates a payment instrument. + + If the payment card, bank account, or alternative payment instrument + already exists it is updated. + requestBody: + $ref: '#/components/requestBodies/PostPaymentInstrument' + responses: + '201': + description: Payment instrument created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentInstrument' + '303': + description: Payment instrument updated. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentInstrument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $paymentInstrumentForm = new + Rebilly\Entities\CommonPaymentInstrument(); + + $paymentInstrumentForm->setCustomerId('customerId'); + + $paymentInstrumentForm->setPan('4111111111111111'); + + $paymentInstrumentForm->setExpYear(2025); + + $paymentInstrumentForm->setExpMonth(8); + + $paymentInstrumentForm->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + + try { + $paymentInstrument = $client->paymentInstruments()->create($paymentInstrumentForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + + + // Alternatively you can specify a payment token + + $paymentInstrumentForm = new + Rebilly\Entities\CommonPaymentInstrument(); + + $paymentInstrumentForm->setCustomerId('customerId'); + + $paymentInstrumentForm->setToken('payment-token'); + + + try { + $paymentInstrument = $client->paymentInstruments()->create($paymentInstrumentForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // first set the properties for the new payment card instrument + + const data = { + method: 'payment-card', + pan: '4111111111111111', + expYear: 2025, + expMonth: 11, + cvv: '123', + billingAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }] + }, + // the customer ID for which + // we are adding a payment card instrument + customerId: 'foobar-0001' + }; + + + const firstPaymentInstrument = await + api.paymentInstruments.create({data}); + + + // alternatively you can specify a payment token + + const tokenData = { + token: 'payment-token', + // the customer ID for which + // we are adding a payment card instrument + customerId: 'foobar-0001' + }; + + + const secondPaymentInstrument = await + api.paymentInstruments.create({tokenData}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + + $paymentInstrumentForm = new + \Rebilly\Sdk\Model\PaymentCardCreatePlain(); + + $paymentInstrumentForm->setCustomerId('customerId'); + + $paymentInstrumentForm->setPan('4111111111111111'); + + $paymentInstrumentForm->setExpYear(2025); + + $paymentInstrumentForm->setExpMonth(8); + + $paymentInstrumentForm->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + + try { + $paymentInstrument = $service->paymentInstruments()->create($paymentInstrumentForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + + + // Alternatively you can specify a payment token + + $paymentInstrumentForm = new + \Rebilly\Sdk\Model\PaymentCardCreatePlain(); + + $paymentInstrumentForm->setCustomerId('customerId'); + + $paymentInstrumentForm->setToken('payment-token'); + + + try { + $paymentInstrument = $service->paymentInstruments()->create($paymentInstrumentForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/payment-instruments/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Payment instruments + summary: Retrieve a payment instrument + operationId: GetPaymentInstrument + x-sdk-operation-name: get + description: Retrieves a payment instrument with a specified ID. + responses: + '200': + description: Payment instrument retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentInstrument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $paymentInstrument = + $client->paymentInstruments()->load('paymentInstrumentId'); + - lang: JavaScript + source: > + const paymentInstrument = await api.paymentInstruments.get({id: + 'payment-instrument-id'}); + + + console.log(paymentInstrument.fields.customerId); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + $paymentInstrument = + $service->paymentInstruments()->get('paymentInstrumentId'); + patch: + x-products: + - Core + tags: + - Payment instruments + summary: Update payment instrument + operationId: PatchPaymentInstrument + x-sdk-operation-name: update + description: Updates the values of a payment instrument with a specified ID. + requestBody: + $ref: '#/components/requestBodies/PatchPaymentInstrument' + responses: + '200': + description: Payment instrument updated. + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentInstrument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $paymentInstrumentForm = new + Rebilly\Entities\CommonPaymentInstrument(); + + $paymentInstrumentForm->setCvv('123'); + + $paymentInstrumentForm->setExpYear(2025); + + $paymentInstrumentForm->setExpMonth(12); + + $paymentInstrumentForm->setBillingAddress([ + 'firstName' => 'John', + ]); + + + try { + $paymentInstrument = $client->paymentInstruments()->update('paymentInstrumentId', $paymentInstrumentForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + + + // Alternatively you can specify a partial token + + $paymentInstrumentForm = new + Rebilly\Entities\CommonPaymentInstrument(); + + $paymentInstrumentForm->setToken('partial-token'); + + $paymentInstrumentForm->setBillingAddress([ + 'firstName' => 'John', + ]); + + + try { + $paymentInstrument = $client->paymentInstruments()->update('paymentInstrumentId', $paymentInstrumentForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: | + // payment instrument properties to be updated + const data = { + cvv: '123', + expMonth: 12, + expYear: 2025, + billingAddress: { + firstName: 'John' + } + }; + + api.paymentInstruments.patch({id: 'id-to-update', data}); + + // alternatively you can specify a partial token + const tokenData = { + token: 'partial-token', + billingAddress: { + firstName: 'John' + }, + customFields: { + foo: 'bar' + } + }; + + api.paymentInstruments.patch({id: 'id-to-update', tokenData}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + + $paymentInstrumentForm = new + \Rebilly\Sdk\Model\PaymentCardUpdatePlain(); + + $paymentInstrumentForm->setCvv('123'); + + $paymentInstrumentForm->setExpYear(2025); + + $paymentInstrumentForm->setExpMonth(12); + + $paymentInstrumentForm->setBillingAddress([ + 'firstName' => 'John', + ]); + + + try { + $paymentInstrument = $service->paymentInstruments()->update('paymentInstrumentId', $paymentInstrumentForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + + + // Alternatively you can specify a partial token + + $paymentInstrumentForm = new + \Rebilly\Sdk\Model\PaymentInstrumentUpdateToken(); + + $paymentInstrumentForm->setToken('partial-token'); + + $paymentInstrumentForm->setBillingAddress([ + 'firstName' => 'John', + ]); + + + try { + $paymentInstrument = $service->paymentInstruments()->update('paymentInstrumentId', $paymentInstrumentForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/payment-instruments/{id}/deactivation': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Payment instruments + summary: Deactivate a payment instrument + operationId: PostPaymentInstrumentDeactivation + x-sdk-operation-name: deactivate + description: Deactivates a payment instrument with a specified ID. + responses: + '201': + description: Payment instrument deactivated. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentInstrument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->paymentInstruments()->deactivate('paymentInstrumentId'); + - lang: JavaScript + source: > + const paymentInstrument = await + api.paymentInstruments.deactivate({id: 'id-to-deactivate'}); + + + console.log(paymentInstrument.fields.status); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $service->paymentInstruments()->deactivate('paymentInstrumentId'); + /plans: + get: + x-products: + - Core + tags: + - Plans + summary: Retrieve plans + operationId: GetPlanCollection + x-sdk-operation-name: getAll + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a list of plans. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of plans retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Plan' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $plans = $client->plans()->search([ + 'filter' => 'name:TestPlan', + ]); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.plans.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.plans.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(plan => + console.log(plan.fields.name)); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $plansPaginator = $service->plans()->getAllPaginator(limit: 5); + + foreach ($plansPaginator as $planPage) { + printf("Plans page %d/%d\n", $plansPaginator->key() + 1, $plansPaginator->count()); + foreach ($planPage as $plan) { + printf("Plan #%s: %s\n", $plan->getId(), $plan->getProductId()); + } + } + + // OR + + $plans = $service->plans()->getAll(limit: 100); + foreach ($plans as $plan) { + printf("Plan #%s: %s\n", $plan->getId(), $plan->getProductId()); + } + post: + x-products: + - Core + tags: + - Plans + summary: Create a plan + operationId: PostPlan + x-sdk-operation-name: create + description: Creates a plan. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Plan' + description: Plan resource. + required: true + responses: + '201': + description: Plan created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Plan' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $planForm = new Rebilly\Entities\Plan(); + $planForm->setName('TestPlan'); + $planForm->setCurrency('USD'); + $planForm->setTrialAmount(1); + $planForm->setTrialPeriodUnit('day'); + $planForm->setTrialPeriodLength(1); + $planForm->setProductId('test-product'); + + try { + $plan = $client->plans()->create($planForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the properties for the new plan + + const data = { + name: 'My strongest plan', + currency: 'USD', + setupAmount: 12.99, + // you could also include a trial if needed + recurringPeriodUnit: 'month', + recurringPeriodLength: 1, + recurringAmount: 25.99 + }; + + + // the ID is optional + + const firstPlan = await api.plans.create({data}); + + + // or you can provide one + + const secondPlan = await api.plans.create({id: 'my-second-key', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $plan = \Rebilly\Sdk\Model\Plan::from([]) + ->setProductId('productId') + ->setName('Test plan') + ->setCurrency('USD') + ->setPricing(new \Rebilly\Sdk\Model\PlanFormulaFlatRate(['price' => 9.99])) + ->setRecurringInterval( + \Rebilly\Sdk\Model\SubscriptionOrderPlanRecurringInterval::from() + ->setUnit(\Rebilly\Sdk\Model\SubscriptionOrderPlanRecurringInterval::UNIT_MONTH) + ->setLength(1), + ); + + try { + $plan = $service->plans()->create($plan); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/plans/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Plans + summary: Retrieve a plan + operationId: GetPlan + x-sdk-operation-name: get + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a plan with a specified ID. + responses: + '200': + description: Plan retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Plan' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $plan = $client->plans()->load('planId'); + - lang: JavaScript + source: |- + const plan = await api.plans.get({id: 'foobar-001'}); + console.log(plan.fields.name); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $plan = $service->plans()->get('planId'); + put: + x-products: + - Core + tags: + - Plans + summary: Upsert a plan + operationId: PutPlan + x-sdk-operation-name: update + description: Creates or updates (upserts) a plan with a specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Plan' + description: Plan resource. + required: true + responses: + '200': + description: Plan updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Plan' + '201': + description: Plan created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Plan' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $planForm = new Rebilly\Entities\Plan(); + $planForm->setName('TestPlan'); + $planForm->setCurrency('USD'); + $planForm->setTrialAmount(1); + $planForm->setTrialPeriodUnit('day'); + $planForm->setTrialPeriodLength(1); + + try { + $plan = $client->plans()->update('planId', $planForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the properties for the new plan + + const data = { + name: 'My strongest plan', + currency: 'USD', + setupAmount: 12.99, + // you could also include a trial if needed + recurringPeriodUnit: 'month', + recurringPeriodLength: 1, + recurringAmount: 25.99 + }; + + + // the ID is optional + + const firstPlan = await api.plans.create({data}); + + + // or you can provide one + + const secondPlan = await api.plans.create({id: 'my-second-key', + data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $plan = new \Rebilly\Sdk\Model\Plan([ + 'recurringInterval' => [ + 'unit' => \Rebilly\Sdk\Model\SubscriptionOrderPlanRecurringInterval::UNIT_WEEK, + 'length' => 2, + ], + ]); + + + try { + $plan = $service->plans()->update('planId', $plan); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + + + // OR + + + $plan = $service->plans()->get('planId'); + + $plan->setRecurringInterval(new + \Rebilly\Sdk\Model\SubscriptionOrderPlanRecurringInterval([ + 'unit' => \Rebilly\Sdk\Model\SubscriptionOrderPlanRecurringInterval::UNIT_WEEK, + 'length' => 2, + ])); + + + try { + $plan = $service->plans()->update('planId', $plan); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Core + tags: + - Plans + summary: Delete a plan + operationId: DeletePlan + x-sdk-operation-name: delete + description: Deletes a plan with a specified ID. + responses: + '204': + description: Plan deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->plans()->delete('planId'); + - lang: JavaScript + source: |- + const request = await api.plans.delete({id: 'my-second-key'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $service->plans()->delete('planId'); + /products: + get: + x-products: + - Core + tags: + - Products + summary: Retrieve products + operationId: GetProductCollection + x-sdk-operation-name: getAll + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a list of products. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of products retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Product' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.products.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.products.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(product => + console.log(product.fields.name)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $productsPaginator = $service->products()->getAllPaginator(limit: + 5); + + foreach ($productsPaginator as $productPage) { + printf("Products page %d/%d\n", $productsPaginator->key() + 1, $productsPaginator->count()); + foreach ($productPage as $product) { + printf("Product #%s: %s\n", $product->getId(), $product->getName()); + } + } + + + // OR + + + $products = $service->products()->getAll(limit: 100); + + foreach ($products as $product) { + printf("Product #%s: %s\n", $product->getId(), $product->getName()); + } + post: + x-products: + - Core + tags: + - Products + summary: Create a product + operationId: PostProduct + x-sdk-operation-name: create + description: Creates a product. + requestBody: + $ref: '#/components/requestBodies/Product' + responses: + '201': + description: Product created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Product' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // first set the properties for the new product + + const data = { + name: 'my first product', + description: 'made to be of the highest quality', + taxCategoryId: '', + requiresShipping: true, + accountingCode: '100', + customFields: [] + }; + + + // the ID is optional + + const firstProduct = await api.products.create({data}); + + + // or you can provide one + + const secondProduct = await api.products.create({id: + 'my-second-key', data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $product = new \Rebilly\Sdk\Model\Product([ + 'name' => 'my first product', + 'description' => 'made to be of the highest quality', + 'taxCategoryId' => '', + 'requiresShipping' => true, + 'accountingCode' => '100', + ]); + + try { + $product = $service->products()->create($product); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/products/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Products + summary: Retrieve a product + operationId: GetProduct + x-sdk-operation-name: get + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a product with a specified ID. + responses: + '200': + description: Product retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Product' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const product = await api.products.get({id: 'foobar-001'}); + console.log(product.fields.name); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $product = $service->products()->get('productId'); + put: + x-products: + - Core + tags: + - Products + summary: Upsert a product + operationId: PutProduct + x-sdk-operation-name: update + description: Creates or updates (upserts) a product with a specified ID. + requestBody: + $ref: '#/components/requestBodies/Product' + responses: + '200': + description: Product updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Product' + '201': + description: Product created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Product' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // creating a new product + + const data = { + name: 'my first product', + description: 'made to be of the highest quality', + taxCategoryId: '', + requiresShipping: true, + accountingCode: '100', + customFields: [] + }; + + + // the ID is optional + + const firstProduct = await api.products.create({data}); + + + // or you can provide one + + const secondProduct = await api.products.create({id: + 'my-second-key', data}); + + + + + // updating a product + + const data = { + name: 'my first product', + description: 'made to be of the highest quality', + taxCategoryId: '20010', + requiresShipping: false, + accountingCode: '77', + customFields: [] + }; + + + const product = await api.products.update({id: 'my-second-key', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $product = new \Rebilly\Sdk\Model\Product([ + 'requiresShipping' => false, + 'accountingCode' => '101', + ]); + + try { + $product = $service->products()->update('productId', $product); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + + // OR + + $product = $service->products()->get('productId'); + $product->setRequiresShipping(false) + ->setAccountingCode('101'); + + try { + $product = $service->products()->update('productId', $product); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Core + tags: + - Products + summary: Delete a product + operationId: DeleteProduct + x-sdk-operation-name: delete + description: Deletes a product with a specified ID. + responses: + '204': + description: Product deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const request = await api.products.delete({id: 'my-second-key'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $service->products()->delete('productId'); + /quotes: + get: + x-products: + - Core + tags: + - Quotes + summary: Retrieve quotes + operationId: GetQuoteCollection + x-sdk-operation-name: getAll + description: Retrieves a list of quotes. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: List of quotes retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Quote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Quotes + summary: Create a quote + operationId: PostQuote + x-sdk-operation-name: create + description: Creates a quote. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + required: true + responses: + '201': + description: Quote created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/quotes/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Quotes + summary: Retrieve a quote + operationId: GetQuote + x-sdk-operation-name: get + description: Retrieves a quote with a specified ID. + parameters: + - $ref: '#/components/parameters/mediaTypeJsonPdf' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Quote retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + application/pdf: + schema: + $ref: '#/components/schemas/Quote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + tags: + - Quotes + summary: Upsert a quote + operationId: PutQuote + x-sdk-operation-name: update + description: >- + Creates or updates a quote with a specified ID. + + + The update operation can only be used for quotes with a `status` of + `draft`. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + required: true + responses: + '200': + description: Quote updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + '201': + description: Quote created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + patch: + x-products: + - Core + tags: + - Quotes + summary: Partially update a quote + operationId: PatchQuote + x-sdk-operation-name: patch + description: >- + Partially updates a quote with a specified ID. + + + The update operation can only be used for quotes with a `status` of + `draft`. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatchQuote' + required: true + responses: + '200': + description: Quote updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + '/quotes/{id}/accept': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Quotes + summary: Accept a quote + operationId: PostQuoteAcceptance + x-sdk-operation-name: accept + description: Accepts an issued quote with a specified ID. + responses: + '200': + description: Quote accepted. + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '/quotes/{id}/cancel': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Quotes + summary: Cancel a quote + operationId: PostQuoteCancellation + x-sdk-operation-name: cancel + description: Cancels a draft or issued quote with a specified ID. + responses: + '200': + description: Quote canceled. + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '/quotes/{id}/issue': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Quotes + summary: Issue a quote + operationId: PostQuoteIssuance + x-sdk-operation-name: issue + description: Issues a draft quote with a specified ID. + responses: + '200': + description: Quote issued. + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '/quotes/{id}/recall': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Quotes + summary: Recall a quote + operationId: PostQuoteRecall + x-sdk-operation-name: recall + description: |- + Recalls an issued quote with a specified ID so that it can be edited. + Recalled quotes are assigned the `status` of `draft`. + responses: + '200': + description: Quote recalled. + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '/quotes/{id}/reject': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Quotes + summary: Reject a quote + operationId: PostQuoteRejection + x-sdk-operation-name: reject + description: Rejects an issued quote with specified ID. + responses: + '200': + description: Quote rejected. + content: + application/json: + schema: + $ref: '#/components/schemas/Quote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '/quotes/{id}/timeline': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Quotes timeline + summary: Retrieve quote timeline messages + operationId: GetQuoteTimelineCollection + x-sdk-operation-name: getAllTimelineMessages + description: Retrieves quote timeline messages. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of quote timeline messages retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/QuoteTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Quotes timeline + summary: Create a quote timeline comment + operationId: PostQuoteTimeline + x-sdk-operation-name: createTimelineComment + description: Creates a quote timeline comment or custom defined event. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QuoteTimeline' + description: Quote timeline resource. + required: true + responses: + '201': + description: Quote timeline comment or custom defined event created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/QuoteTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/quotes/{id}/timeline/{messageId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: messageId + in: path + description: ID of the quote timeline message. + required: true + schema: + type: string + get: + x-products: + - Core + tags: + - Quotes timeline + summary: Retrieve a quote timeline message + operationId: GetQuoteTimelineMessage + x-sdk-operation-name: getTimelineMessage + description: Retrieves a quote timeline message with a specified ID. + responses: + '200': + description: Quote timeline message retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/QuoteTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Core + tags: + - Quotes timeline + summary: Delete a quote timeline message + operationId: DeleteQuoteTimeline + x-sdk-operation-name: deleteTimelineMessage + description: Deletes a quote timeline message with a specified ID. + responses: + '204': + description: Quote timeline message deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + /ready-to-pay: + post: + x-products: + - Core + tags: + - Transactions + summary: Ready to pay + operationId: PostReadyToPay + x-sdk-operation-name: readyToPay + description: >- + Retrieves available payment methods for a specific transaction or + purchase. + + + The order in which the payment methods are displayed to the customer + should be the same as the order in the response. + + + The list of payment methods is generated from available gateway accounts + and the last matched `adjust-ready-to-pay` action on the + `ready-to-pay-requested` event. + + If no rules match for the specific request, all methods supported by the + gateway accounts are sent. + + + To invert this behavior, place an all matching rule at the end of the + `ready-to-pay-requested` event in the rules engine, + + and include an empty `paymentMethods` property for the + `adjust-ready-to-pay` action. + + + For more information, see [Update event + rules](https://all-rebilly.redoc.ly/tag/Rules#operation/PutEventRule) + and [Gateway + accounts](https://all-rebilly.redoc.ly/tag/Gateway-accounts). + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PostReadyToPay' + responses: + '200': + description: Payment methods retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ReadyToPayMethods' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $readyToPayForm = new Rebilly\Entities\ReadyToPay(); + $readyToPayForm->setCurrency('USD'); + $readyToPayForm->setAmount(10); + $readyToPayForm->setWebsiteId('websiteId'); + $readyToPayForm->setCustomerId('customerId'); + + $riskMetadata = new Rebilly\Entities\RiskMetadata(); + $riskMetadata->setFingerprint('fingerprint'); + $readyToPayForm->setRiskMetadata($riskMetadata); + + try { + $transaction = $client->transactions()->create($readyToPayForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $readyToPayForm = new \Rebilly\Sdk\Model\PostReadyToPay(); + $readyToPayForm->setCurrency('USD'); + $readyToPayForm->setAmount(10); + $readyToPayForm->setWebsiteId('websiteId'); + $readyToPayForm->setCustomerId('customerId'); + + $riskMetadata = new \Rebilly\Sdk\Model\RiskMetadata(); + $riskMetadata->setFingerprint('fingerprint'); + $readyToPayForm->setRiskMetadata($riskMetadata); + + try { + $transactions = $service->purchase()->readyToPay($readyToPayForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + /search: + get: + x-products: + - Core + tags: + - Search + summary: Search merchant data + operationId: GetSearch + x-sdk-operation-name: get + description: >- + Searches merchant data to return resources such as customers, invoices, + orders, transactions. + parameters: + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - name: q + in: query + description: Default search. Searches across resources and fields. + schema: + type: string + responses: + '200': + description: Results keyed by a resource. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Search' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + /shipping-rates: + get: + x-products: + - Core + tags: + - Shipping rates + summary: Retrieve shipping rates + operationId: GetShippingRateCollection + x-sdk-operation-name: getAll + description: Retrieves a list of shipping rates. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of shipping rates retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ShippingRate' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await api.shippingRates.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.shippingRates.getAll(params); + + + // access the collection items, each item is a ShippingRate + + secondCollection.items.forEach(shippingRate => + console.log(shippingRate.fields.name)); + post: + x-products: + - Core + tags: + - Shipping rates + summary: Create a shipping rate + operationId: PostShippingRate + x-sdk-operation-name: create + description: Creates a shipping rate. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ShippingRate' + description: Shipping rate resource. + required: true + responses: + '201': + description: Shipping rate created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ShippingRate' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: > + // first set the properties for the new shipping rate + + const data = { + name: 'free shipping', + filter: '', + price: 0, + currency: 'USD' + }; + + + // the ID is optional + + const firstRate = await api.shippingRates.create({data}); + + + // or you can provide one + + const secondRate = await api.shippingRates.create({id: + 'my-second-key', data}); + '/shipping-rates/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Shipping rates + summary: Retrieve a shipping rate + operationId: GetShippingRate + x-sdk-operation-name: get + description: Retrieves a shipping rate with a specified ID. + responses: + '200': + description: Shipping rate retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ShippingRate' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: > + const shippingRate = await api.shippingRates.get({id: + 'foobar-001'}); + + console.log(shippingRate.fields.name); + put: + x-products: + - Core + tags: + - Shipping rates + summary: Create a shipping rate with ID + operationId: PutShippingRate + x-sdk-operation-name: update + description: Creates a shipping rate with a specified ID. + responses: + '200': + description: Shipping rate updated. + content: + application/json: + schema: + $ref: '#/components/schemas/ShippingRate' + '201': + description: Shipping rate created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ShippingRate' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ShippingRate' + description: Shipping rate resource. + required: true + x-codeSamples: + - lang: JavaScript + source: > + // creating a new shipping rate + + const data = { + name: 'free shipping', + filter: '', + price: 0, + currency: 'USD' + }; + + + // the ID is optional + + const firstRate = await api.shippingRates.create({data}); + + + // or you can provide one + + const secondRate = await api.shippingRates.create({id: + 'my-second-key', data}); + + + + // updating a shipping rate + + const data = { + name: 'regular shipping', + filter: '', + price: 9.99, + currency: 'USD' + }; + + + const shippingRate = await api.shippingRates.update({id: + 'my-second-key', data}); + delete: + x-products: + - Core + tags: + - Shipping rates + summary: Delete a shipping rate + operationId: DeleteShippingRate + x-sdk-operation-name: delete + description: Deletes a shipping rate with a specified ID. + responses: + '204': + description: Shipping rate deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: > + const request = await api.shippingRates.delete({id: + 'my-second-key'}); + + + // the request does not return any fields but + + // you can confirm the success using the status code + + console.log(request.response.status); // 204 + /subscriptions: + get: + x-products: + - Core + tags: + - Orders + summary: Retrieve orders + operationId: GetSubscriptionCollection + x-sdk-operation-name: getAll + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a list of orders. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/subscriptionExpand' + responses: + '200': + description: List of subscriptions retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Subscription' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $subscriptions = $client->subscriptions()->search([ + 'filter' => 'customerId:testCustomerId', + ]); + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await api.subscriptions.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.subscriptions.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(subscription => + console.log(subscription.fields.customerId)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $ordersPaginator = + $service->subscriptions()->getAllPaginator(filter: + 'customerId:testCustomerId'); + + foreach ($ordersPaginator as $orderPage) { + printf("Orders page %d/%d\n", $ordersPaginator->key() + 1, $ordersPaginator->count()); + foreach ($orderPage as $order) { + printf("Order #%s (%s): %s\n", $order->getId(), $order->getOrderType(), $order->getStatus()); + } + } + + + // OR + + + $orders = $service->subscriptions()->getAll(filter: + 'customerId:testCustomerId', limit: 10); + + foreach ($orders as $order) { + printf("Order #%s (%s): %s\n", $order->getId(), $order->getOrderType(), $order->getStatus()); + } + post: + x-products: + - Core + tags: + - Orders + summary: Create an order + operationId: PostSubscription + x-sdk-operation-name: create + description: >- + Creates an order. + + + To create or update an order with a specified ID, use the [Upsert an + order](../PutSubscription) operation. + parameters: + - $ref: '#/components/parameters/subscriptionExpand' + requestBody: + $ref: '#/components/requestBodies/Subscription' + responses: + '201': + description: Order created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Subscription' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $subscriptionForm = new Rebilly\Entities\Subscription(); + $subscriptionForm->setCustomerId('customerId'); + $subscriptionForm->setWebsiteId('websiteId'); + $subscriptionForm->setItems([ + [ + 'planId' => 'my-plan', + 'quantity' => 1, + ], + ]); + $subscriptionForm->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + try { + $subscription = $client->subscriptions()->create($subscriptionForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the properties for the new subscription + + const data = { + customerId: 'foobar-0001', + websiteId: 'my-main-website', + items: [ + { planId: 'my-plan-id', quantity: '1' }, + ], + // you can append this subscription to + // an existing invoice by passing its ID + initialInvoiceId: 'my-existing-invoice-id', + billingAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }], + }, + deliveryAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }], + }, + quantity: 1, + customFields: {} + }; + + + // the ID is optional + + const firstInvoice = await api.subscriptions.create({data}); + + + // or you can provide one + + const secondInvoice = await api.subscriptions.create({id: + 'my-second-id', data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $order = \Rebilly\Sdk\Model\SubscriptionOrder::from() + ->setWebsiteId('websiteId') + ->setCustomerId('customerId') + ->setItems([ + \Rebilly\Sdk\Model\OrderItem::from() + ->setPlan( + \Rebilly\Sdk\Model\OrderItemPlan::from() + ->setId('planId') + ) + ->setQuantity(1), + ]); + + try { + $order = $service->subscriptions()->create($order); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/subscriptions/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Orders + summary: Retrieve an order + operationId: GetSubscription + x-sdk-operation-name: get + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + parameters: + - $ref: '#/components/parameters/subscriptionExpand' + description: Retrieves an order with a specified ID. + responses: + '200': + description: Order retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Subscription' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $subscription = $client->subscriptions()->load('subscriptionId'); + - lang: JavaScript + source: >- + const subscription = await api.subscriptions.get({id: + 'foobar-001'}); + + console.log(subscription.fields.billingAddress.firstName); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $order = $service->subscriptions()->get('orderId'); + put: + x-products: + - Core + tags: + - Orders + summary: Upsert an order + operationId: PutSubscription + x-sdk-operation-name: update + description: Creates or updates (upserts) an order with a specified ID. + parameters: + - $ref: '#/components/parameters/subscriptionExpand' + requestBody: + $ref: '#/components/requestBodies/Subscription' + responses: + '200': + description: Order updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Subscription' + '201': + description: Order created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Subscription' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $subscriptionForm = new Rebilly\Entities\Subscription(); + $subscriptionForm->setCustomerId('customerId'); + $subscriptionForm->setWebsiteId('websiteId'); + $subscriptionForm->setItems($subscriptionForm->createItems([ + [ + 'planId' => 'my-plan', + 'quantity' => 1, + ], + ])); + $subscriptionForm->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + try { + $subscription = $client->subscriptions()->update('subscriptionId', $subscriptionForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // creating a subscription + + const data = { + customerId: 'foobar-0001', + websiteId: 'my-main-website', + planId: 'my-plan-id', + // you can append this subscription to + // an existing invoice by passing its ID + initialInvoiceId: 'my-existing-invoice-id', + billingAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }], + }, + deliveryAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }], + }, + quantity: 1, + customFields: {} + }; + + + // the ID is optional + + const firstInvoice = await api.subscriptions.create({data}); + + + // or you can provide one + + const secondInvoice = await api.subscriptions.create({id: + 'my-second-id', data}); + + + + + // updating a subscription + + const data = { + // determines if a payment attempt will be automatic + autopay: false, + // set the next renewal time + renewalTime: '2018-09-26T15:34:29Z' + }; + + + const subscription = await api.subscriptions.update({id: + 'my-second-id', data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $order = new \Rebilly\Sdk\Model\SubscriptionOrder([ + 'shipping' => new \Rebilly\Sdk\Model\ManualShipping([ + 'amount' => 14.99, + ]), + ]); + + + try { + $order = $service->subscriptions()->update('orderId', $order); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + + + // OR + + + $order = $service->subscriptions()->get('orderId'); + + $order->setShipping(new \Rebilly\Sdk\Model\ManualShipping(['amount' + => 14.99])); + + + try { + $order = $service->subscriptions()->update('orderId', $order); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Core + tags: + - Orders + summary: Delete a pending order + operationId: DeleteSubscription + x-sdk-operation-name: delete + description: Deletes a pending order with a specified ID. + responses: + '204': + description: Pending order deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $service->subscriptions()->delete('orderId'); + '/subscriptions/{id}/void': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Orders + summary: Void a subscription order + operationId: PostSubscriptionVoid + x-sdk-operation-name: void + description: Voids a pending order with a specified ID. + responses: + '201': + description: Order voided. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Subscription' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $subscription = $client->subscriptions()->void('subscriptionId'); + - lang: JavaScript + source: > + const voidedSubscription = await api.subscriptions.void({id: + 'my-subscription-id'}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $order = $service->subscriptions()->void('orderId'); + '/subscriptions/{id}/change-items': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Orders + summary: Change subscription order items + operationId: PostSubscriptionItemsChange + x-sdk-operation-name: changeItems + description: >- + Changes subscription order items or quantities, and designates if or + when pro-rata credits should be given. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionChange' + description: Change items request. + required: true + responses: + '201': + description: Order changed. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Subscription' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/subscriptions/{id}/interim-invoice': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Orders + summary: Issue an interim invoice + operationId: PostSubscriptionInterimInvoice + x-sdk-operation-name: createInterimInvoice + description: |- + Issues an interim invoice for a subscription order. + Use this operation for plan changes and pro rata adjustments. + + This process: + + - Creates an invoice. + - Adds the subscription line items to the invoice. + - Issues the invoice. + - Optionally, applies payment to the invoice if a `transactionId` is supplied. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionInvoice' + description: Issue an interim invoice. + required: true + responses: + '201': + description: Invoice created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /subscription-pauses: + get: + x-products: + - Core + tags: + - Orders + summary: Retrieve subscription order pauses + operationId: GetSubscriptionPauseCollection + x-sdk-operation-name: getAll + description: Retrieves a list of subscription order pauses. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of subscription order pauses retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SubscriptionPause' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Orders + summary: Pause a subscription order + operationId: PostSubscriptionPause + x-sdk-operation-name: pause + description: Pauses a subscription order with a specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionPause' + description: Pause resource. + required: true + responses: + '201': + description: Pause created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionPause' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/subscription-pauses/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Orders + summary: Retrieve a subscription order pause + operationId: GetSubscriptionPause + x-sdk-operation-name: get + description: Retrieves a subscription order pause with a specified ID. + responses: + '200': + description: Pause retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionPause' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + tags: + - Orders + summary: Update a subscription order pause + operationId: PutSubscriptionPause + x-sdk-operation-name: update + description: Updates a subscription order pause. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionPause' + responses: + '200': + description: Pause updated. + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionPause' + '201': + description: Pause created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionPause' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Core + tags: + - Orders + summary: Delete a subscription order pause + operationId: DeleteSubscriptionPause + x-sdk-operation-name: delete + description: |- + Deletes a subscription order pause with a specified ID. + If the specified pause is `pending`, it is `revoked`. + If the specified pause is `ongoing`, it is `finished`. + responses: + '204': + description: Subscription order pause revoked or finished. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + /subscription-cancellations: + get: + x-products: + - Core + tags: + - Orders + summary: Retrieve subscription order cancellations + operationId: GetSubscriptionCancellationCollection + x-sdk-operation-name: getAll + description: Retrieves a list of subscription order cancellations. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of cancelled subscription orders retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SubscriptionCancellation' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $cancellationsPaginator = + $service->subscriptionCancellations()->getAllPaginator( + filter: 'reason:' . \Rebilly\Sdk\Model\SubscriptionCancellation::REASON_CONTRACT_EXPIRED, + ); + + foreach ($cancellationsPaginator as $cancellationPage) { + printf("Cancellations page %d/%d\n", $cancellationsPaginator->key() + 1, $cancellationsPaginator->count()); + foreach ($cancellationPage as $cancellation) { + printf( + "Cancellation #%s (%s): %s\n", + $cancellation->getId(), + $cancellation->getStatus(), + $cancellation->getSubscriptionId(), + ); + } + } + + + // OR + + + $cancellations = $service->subscriptionCancellations()->getAll( + filter: 'reason:' . \Rebilly\Sdk\Model\SubscriptionCancellation::REASON_CONTRACT_EXPIRED, + limit: 10, + ); + + foreach ($cancellations as $cancellation) { + printf( + "Cancellation #%s (%s): %s\n", + $cancellation->getId(), + $cancellation->getStatus(), + $cancellation->getSubscriptionId(), + ); + } + post: + x-products: + - Core + tags: + - Orders + summary: Cancel a subscription order + operationId: PostSubscriptionCancellation + x-sdk-operation-name: create + description: Cancels a subscription order. + requestBody: + $ref: '#/components/requestBodies/SubscriptionCancellation' + responses: + '201': + description: Cancellation created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionCancellation' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $subscriptionCancellationForm = new + Rebilly\Entities\SubscriptionCancellation(); + + $subscriptionCancellationForm->setSubscriptionId('subscription-1'); + + $subscriptionCancellationForm->setChurnTime(date('c')); + + + try { + $subscription = $client->subscriptionCancellations()->create($subscriptionCancellationForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + const data = { + subscriptionId: 'ord_01GYJPRKHBD6ZYHH897QCJMBS4', + churnTime: '2020-06-10T13:55:51Z', + } + + + // the ID is optional + + const firstSubscriptionCancellation = await + api.subscriptionCancellations.create({data}); + + + // or you can provide one + + const secondSubscriptionCancellation = await + api.subscriptionCancellations.create({id: 'custom-cancellation-id', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $cancellation = new \Rebilly\Sdk\Model\SubscriptionCancellation([ + 'subscriptionId' => 'subscriptionId', + 'churnTime' => new \DateTimeImmutable(), + 'canceledBy' => \Rebilly\Sdk\Model\SubscriptionCancellation::CANCELED_BY_MERCHANT, + 'reason' => \Rebilly\Sdk\Model\SubscriptionCancellation::REASON_CONTRACT_EXPIRED, + ]); + + try { + $cancellation = $service->subscriptionCancellations()->create($cancellation); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/subscription-cancellations/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Orders + summary: Retrieve a subscription order cancellation + operationId: GetSubscriptionCancellation + x-sdk-operation-name: get + description: Retrieves a subscription order cancellation with a specified ID. + responses: + '200': + description: Subscription order cancellation retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionCancellation' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + $cancellation = + $service->subscriptionCancellations()->get('cancellationId'); + put: + x-products: + - Core + tags: + - Orders + summary: Upsert a subscription order cancellation + operationId: PutSubscriptionCancellation + x-sdk-operation-name: update + description: >- + Creates or updates (upserts) a subscription order cancellation with a + specified ID. + requestBody: + $ref: '#/components/requestBodies/SubscriptionCancellation' + responses: + '200': + description: Cancellation updated. + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionCancellation' + '201': + description: Cancellation created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionCancellation' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: > + const data = { + subscriptionId: 'ord_01GYJPRKHBD6ZYHH897QCJMBS4', + churnTime: '2020-06-10T13:55:51Z', + } + + + // the ID is optional + + const firstSubscriptionCancellation = await + api.subscriptionCancellations.create({data}); + + + // or you can provide one + + const secondSubscriptionCancellation = await + api.subscriptionCancellations.create({id: 'custom-cancellation-id', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $cancellation = new \Rebilly\Sdk\Model\SubscriptionCancellation([ + 'subscriptionId' => 'subscriptionId', + 'churnTime' => new \DateTimeImmutable(), + 'canceledBy' => \Rebilly\Sdk\Model\SubscriptionCancellation::CANCELED_BY_MERCHANT, + 'reason' => \Rebilly\Sdk\Model\SubscriptionCancellation::REASON_CONTRACT_EXPIRED, + ]); + + try { + $cancellation = $service->subscriptionCancellations()->update('cancellationId', $cancellation); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + patch: + x-products: + - Core + tags: + - Orders + summary: Update a subscription order cancellation + operationId: PatchSubscriptionCancellation + x-sdk-operation-name: patch + description: |- + Updates a subscription order cancellation with a specified ID. + Use this operation to update a cancellation reason and description. + requestBody: + content: + application/json: + schema: + allOf: + - properties: + canceledBy: + readOnly: true + status: + readOnly: true + churnTime: + readOnly: true + - $ref: '#/components/schemas/SubscriptionCancellation' + description: Patch cancellation resource. + required: true + responses: + '200': + description: Cancellation updated. + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionCancellation' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $cancellation = new + \Rebilly\Sdk\Model\PatchSubscriptionCancellationRequest([ + 'reason' => \Rebilly\Sdk\Model\PatchSubscriptionCancellationRequest::REASON_CONTRACT_EXPIRED, + ]); + + + try { + $cancellation = $service->subscriptionCancellations()->patch('cancellationId', $cancellation); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Core + tags: + - Orders + summary: Delete a subscription order cancellation + operationId: DeleteSubscriptionCancellation + x-sdk-operation-name: delete + description: >- + Deletes a subscription order cancellation with a specified ID. + + + > **Important:** Only subscription order cancellations with a `status` + of `draft` can be deleted. + responses: + '204': + description: Subscription order cancellation deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $service->subscriptionCancellations()->delete('cancellationId'); + /subscription-reactivations: + get: + x-products: + - Core + tags: + - Orders + summary: Retrieve subscription order reactivations + operationId: GetSubscriptionReactivationCollection + x-sdk-operation-name: getAll + description: Retrieves a list of subscription order reactivations. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of subscription order reactivations retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SubscriptionReactivation' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await + api.subscriptionReactivations.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await + api.subscriptionReactivations.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(subscription => + console.log(subscription.fields.customerId)); + post: + x-products: + - Core + tags: + - Orders + summary: Reactivate a subscription order + operationId: PostSubscriptionReactivation + x-sdk-operation-name: reactivate + description: Reactivates a subscription order with a specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionReactivation' + description: Reactivation resource. + required: true + responses: + '201': + description: >- + Reactivation created. The order is now active. + + If the order had a related cancellation with a `status` of + `confirmed`, it is revoked. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionReactivation' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: > + const data = { + subscriptionId: 'ord_01GYJPRKHBD6ZYHH897QCJMBS4', + description: 'reactivation-reason', + effectiveTime: '2020-06-10T13:55:51Z', + renewalTime: '2020-07-10T13:55:51Z' + }; + + + const reactivatedSubscription = await + api.subscriptionReactivations.reactivate({data}); + '/subscription-reactivations/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Orders + summary: Retrieve a subscription order reactivation + operationId: GetSubscriptionReactivation + x-sdk-operation-name: get + description: Retrieves a subscription order reactivation with a specified ID. + responses: + '200': + description: Reactivation retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionReactivation' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: > + const subscription = await api.subscriptionReactivations.get({id: + 'foobar-001'}); + + console.log(subscription.fields.description); + '/subscriptions/{id}/upcoming-invoices': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Orders + summary: Retrieve upcoming subscription order invoice + operationId: GetSubscriptionUpcomingInvoiceCollection + x-sdk-operation-name: getAllUpcomingInvoices + description: >- + Retrieves an upcoming invoice for a specified subscription order. + + + This endpoint is deprecated. + + Use [Retrieve an upcoming invoice](../GetSubscriptionUpcomingInvoice) + instead. + parameters: + - $ref: '#/components/parameters/collectionExpand' + deprecated: true + responses: + '200': + description: Upcoming invoices retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UpcomingInvoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/subscriptions/{id}/upcoming-invoice': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Orders + summary: Retrieve an upcoming subscription order invoice + operationId: GetSubscriptionUpcomingInvoice + x-sdk-operation-name: getUpcomingInvoice + description: >- + Retrieves an upcoming invoice for a specified subscription order. + + + An upcoming invoice is an invoice that has not been issued. + + It functions as a preview of the next invoice for the order. + + For more information, see + [Invoices](https://www.rebilly.com/docs/dev-docs/invoices/). + parameters: + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Upcoming invoice retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/UpcomingInvoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/subscriptions/{id}/upcoming-invoice/issue': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Orders + summary: Issue an upcoming invoice for early pay + operationId: PostSubscriptionUpcomingInvoiceIssuance + x-sdk-operation-name: issueEarlyUpcomingInvoice + description: >- + Issues an upcoming invoice, for a subscription order with a specified + ID, for early pay. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceIssue' + description: InvoiceIssue resource. + required: true + responses: + '201': + description: Upcoming invoice issued. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/UpcomingInvoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '/subscriptions/{id}/upcoming-invoices/{invoiceId}/issue': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: invoiceId + in: path + description: ID of the upcoming invoice. + required: true + schema: + type: string + post: + x-products: + - Core + tags: + - Orders + summary: Issue a specific upcoming invoice for early pay + operationId: PostUpcomingInvoiceIssuance + x-sdk-operation-name: issueUpcomingInvoice + deprecated: true + description: >- + Issues an upcoming invoice with a specified ID for early pay. + + + This endpoint is deprecated. + + Use [Issue an upcoming invoice for early + pay](../PostSubscriptionUpcomingInvoiceIssuance) instead. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InvoiceIssue' + description: InvoiceIssue resource. + required: true + responses: + '201': + description: Upcoming invoice issued. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/UpcomingInvoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '/subscriptions/{id}/timeline': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Orders timeline + summary: Retrieve order timeline messages + operationId: GetSubscriptionTimelineCollection + x-sdk-operation-name: getAllTimelineMessages + description: Retrieves a list of order timeline messages. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of order timeline messages retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/OrderTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional except for the `id` + + const firstCollection = await api.subscriptions + .getAllTimelineMessages({id: 'my-transaction'}); + + // alternatively you can specify one or more of them + + const params = {id: 'my-transaction', limit: 20, offset: 100}; + + const secondCollection = await + api.subscriptions.getAllTimelineMessages(params); + + + // access the collection items, each item is a Member + + secondCollection.items + .forEach(message => console.log(message.fields.eventType)); + post: + x-products: + - Core + tags: + - Orders timeline + summary: Create an order timeline comment + operationId: PostSubscriptionTimeline + x-sdk-operation-name: createTimelineComment + description: Creates an order timeline comment. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/OrderTimeline' + description: Order Timeline resource. + required: true + responses: + '201': + description: Order timeline comment created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/OrderTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // Create a comment + + const firstComment = await api + .subscriptions.createTimelineComment({id: 'my-subscription-id', data: {message: 'Your comment here'}}); + + // Using params object, mentions and references + + const message = `Example of mentions @user@mydomain.com and + references #subscriptions-subscription-id`; + + const params = { + id: 'my-subscription-id', + data: { + message, + }, + }; + + const secondComment = await + api.subscriptions.createTimelineComment(params); + '/subscriptions/{id}/timeline/{messageId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: messageId + in: path + description: ID of the order timeline message. + required: true + schema: + type: string + get: + x-products: + - Core + tags: + - Orders timeline + summary: Retrieve an order timeline message + operationId: GetSubscriptionTimeline + x-sdk-operation-name: getTimelineMessage + description: Retrieves a order message with a specified ID. + responses: + '200': + description: Order timeline message retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/OrderTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const message = await api.subscriptions + .getTimelineMessage({id: 'foobar-001', messageId: 'message-202'}); + console.log(message.fields.eventType); + delete: + x-products: + - Core + tags: + - Orders timeline + summary: Delete an order timeline message + operationId: DeleteSubscriptionTimeline + x-sdk-operation-name: deleteTimelineMessage + description: Deletes an order timeline message with a specified ID. + responses: + '204': + description: Order timeline message deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: JavaScript + source: |- + const request = await api.subscriptions + .deleteTimelineMessage({id: 'foobar-001', messageId: 'message-202'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + /usages: + get: + x-products: + - Core + tags: + - Usage + summary: Retrieve usage records + operationId: GetUsageCollection + x-sdk-operation-name: getAll + description: Retrieves a list of usage records. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of usage records retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Usage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Usage + summary: Create a usage record + operationId: PostUsage + x-sdk-operation-name: create + description: Creates a usage report. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Usage' + responses: + '201': + description: Usage record created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Usage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/usages/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Usage + summary: Retrieve a usage record + operationId: GetUsage + x-sdk-operation-name: get + security: + - SecretApiKey: [] + - JWT: [] + description: Retrieves a usage record with a specified ID. + responses: + '200': + description: Usage record retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Usage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + tags: + - Usage + summary: Upsert a usage record + operationId: PutUsage + x-sdk-operation-name: update + description: Creates or updates a usage record with a specified ID. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Usage' + responses: + '200': + description: Usage record updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Usage' + '201': + description: Usage record created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Usage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Core + tags: + - Usage + summary: Delete a usage record + operationId: DeleteUsage + x-sdk-operation-name: delete + description: Deletes a usage record with a specified ID. + responses: + '204': + description: Usage record deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + /tags: + get: + x-products: + - Core + tags: + - Tags + summary: Retrieve tags + operationId: GetTagCollection + x-sdk-operation-name: getAll + description: Retrieves a list of tags. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of tags retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Tag' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $tagsPaginator = $service->tags()->getAllPaginator(limit: 5); + foreach ($tagsPaginator as $tagPage) { + printf("Tags page %d/%d\n", $tagsPaginator->key() + 1, $tagsPaginator->count()); + foreach ($tagPage as $tag) { + printf("Tag #%s: %s\n", $tag->getId(), $tag->getName()); + } + } + + // OR + + $tags = $service->tags()->getAll(); + foreach ($tags as $tag) { + printf("Tag #%s: %s\n", $tag->getId(), $tag->getName()); + } + post: + x-products: + - Core + tags: + - Tags + summary: Create a tag + operationId: PostTag + x-sdk-operation-name: create + description: Creates a tag. + requestBody: + $ref: '#/components/requestBodies/Tag' + responses: + '201': + description: Tag created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Tag' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $tagForm = new \Rebilly\Sdk\Model\Tag(); + $tagForm->setName('tag-name'); + $tagForm->setType(\Rebilly\Sdk\Model\Tag::TYPE_CUSTOMER); + + try { + $service->tags()->create($tagForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/tags/{tag}': + parameters: + - $ref: '#/components/parameters/tag' + get: + x-products: + - Core + tags: + - Tags + summary: Retrieve a tag + operationId: GetTag + x-sdk-operation-name: get + description: Retrieves a tag with a specified name. + responses: + '200': + description: Tag with a specified name retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Tag' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + patch: + x-products: + - Core + tags: + - Tags + summary: Update a tag + operationId: PatchTag + x-sdk-operation-name: update + description: Updates a tag with a specified name. + requestBody: + $ref: '#/components/requestBodies/UpdateTag' + responses: + '200': + description: Tag updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Tag' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Core + tags: + - Tags + summary: Delete a tag + operationId: DeleteTag + x-sdk-operation-name: delete + description: |- + Deletes a tag with a specified name. + This is an asynchronous operation. + responses: + '204': + description: Tag deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/tags/{tag}/customers': + parameters: + - $ref: '#/components/parameters/tag' + post: + x-products: + - Core + tags: + - Tags + summary: Tag a list of customers + operationId: PostTagCustomerCollection + x-sdk-operation-name: tagCustomers + description: >- + Tags a list of customers. + + + If a customer in the list already has the specified tag applied, the + customer is ignored. + + This is an asynchronous operation. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - customerIds + properties: + customerIds: + description: List of customer IDs. + type: array + minItems: 1 + maxItems: 1000 + items: + type: string + responses: + '204': + description: Customers tagging process has been scheduled. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Core + tags: + - Tags + summary: Untag a list of customers + operationId: DeleteTagCustomerCollection + x-sdk-operation-name: untagCustomers + description: >- + Untag a list of customers. + + + If a customer in the list does not have the specified tag applied, the + customer is ignored. + + This is an asynchronous operation. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - customerIds + properties: + customerIds: + description: List of customer IDs. + type: array + minItems: 1 + maxItems: 1000 + items: + type: string + responses: + '204': + description: Customers untagging process has been scheduled. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/tags/{tag}/customers/{customerId}': + parameters: + - $ref: '#/components/parameters/tag' + - $ref: '#/components/parameters/customerId' + post: + x-products: + - Core + tags: + - Tags + summary: Tag a customer + operationId: PostTagCustomer + x-sdk-operation-name: tagCustomer + description: Tags a customer. + responses: + '204': + description: Customer tagged. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Core + tags: + - Tags + summary: Untag a customer + operationId: DeleteTagCustomer + x-sdk-operation-name: untagCustomer + description: Untags a customer. + responses: + '204': + description: Customer untagged. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/tags/{tag}/kyc-documents': + parameters: + - $ref: '#/components/parameters/tag' + post: + x-products: + - Core + tags: + - Tags + summary: Tag a list of KYC documents + operationId: PostTagKycDocumentCollection + x-sdk-operation-name: tagKycDocuments + description: >- + Tags a list of KYC documents. + + + If a KYC document in the list already has the specified tag applied, the + KYC document is ignored. + + This is an asynchronous operation. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - kycDocumentIds + properties: + kycDocumentIds: + description: List of KYC document IDs. + type: array + minItems: 1 + maxItems: 1000 + items: + type: string + example: kyc_doc_0YV7JHY705C6DA487BFTAA33V8 + responses: + '204': + description: KYC documents tagging process has been scheduled. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Core + tags: + - Tags + summary: Untag a list of KYC documents + operationId: DeleteTagKycDocumentCollection + x-sdk-operation-name: untagKycDocuments + description: >- + Untags a list of KYC documents. + + + If a KYC document in the list does not have the specified tag applied, + the KYC document is ignored. + + This is an asynchronous operation. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - kycDocumentIds + properties: + kycDocumentIds: + description: List of KYC document IDs. + type: array + minItems: 1 + maxItems: 1000 + items: + type: string + example: kyc_doc_0YV7JHY705C6DA487BFTAA33V8 + responses: + '204': + description: KYC documents untagging process has been scheduled. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/tags/{tag}/kyc-documents/{kycDocumentId}': + parameters: + - $ref: '#/components/parameters/tag' + - $ref: '#/components/parameters/kycDocumentId' + post: + x-products: + - Core + tags: + - Tags + summary: Tag a KYC document + operationId: PostTagKycDocument + x-sdk-operation-name: tagKycDocument + description: Tags a KYC document. + responses: + '204': + description: KYC document tagged. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Core + tags: + - Tags + summary: Untag a KYC document + operationId: DeleteTagKycDocument + x-sdk-operation-name: untagKycDocument + description: Untags a KYC document. + responses: + '204': + description: KYC document untagged. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/tags/{tag}/aml-checks': + parameters: + - $ref: '#/components/parameters/tag' + post: + x-products: + - Core + tags: + - Tags + summary: Tag a list of AML checks + operationId: PostTagAmlCheckCollection + x-sdk-operation-name: tagAmlChecks + description: >- + Tags a list of AML checks. + + + If an AML check in the list already has the specified tag applied, the + AML check is ignored. + + This is an asynchronous operation. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - amlCheckIds + properties: + amlCheckIds: + description: List of AML check IDs to tag. + type: array + minItems: 1 + maxItems: 1000 + items: + type: string + example: aml_chk_0YV7JHY705C6DA487BFTAA33V8 + responses: + '204': + description: AML checks tagging process has been scheduled. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Core + tags: + - Tags + summary: Untag a list of AML checks + operationId: DeleteTagAmlCheckCollection + x-sdk-operation-name: untagAmlChecks + description: >- + Untags a list of AML checks. + + + If an AML check in the list does not have the specified tag applied, the + AML check is ignored. + + This is an asynchronous operation. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - amlCheckIds + properties: + amlCheckIds: + description: List of AML check IDs to tag. + type: array + minItems: 1 + maxItems: 1000 + items: + type: string + example: aml_chk_0YV7JHY705C6DA487BFTAA33V8 + responses: + '204': + description: AML checks untagging process has been scheduled. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/tags/{tag}/aml-checks/{amlCheckId}': + parameters: + - $ref: '#/components/parameters/tag' + - name: amlCheckId + in: path + description: ID of the AML check to tag. + required: true + schema: + type: string + post: + x-products: + - Core + tags: + - Tags + summary: Tag an AML check + operationId: PostTagAmlCheck + x-sdk-operation-name: tagAmlCheck + description: >- + Tags an AML check. + + + If an AML check in the list does not have the specified tag applied, the + AML check is ignored. + responses: + '204': + description: AML check tagged. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Core + tags: + - Tags + summary: Untag an AML check + operationId: DeleteTagAmlCheck + x-sdk-operation-name: untagAmlCheck + description: >- + Untags an AML check. + + + If an AML check in the list does not have the specified tag applied, the + AML check is ignored. + responses: + '204': + description: AML check untagged. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /tokens: + post: + x-products: + - Core + tags: + - Payment tokens + summary: Create a payment token + operationId: PostToken + x-sdk-operation-name: create + description: >- + Creates a payment token which can be exchanged into a payment + instrument. + + [FramePay](https://docs.rebilly.com/docs/developer-docs/framepay/) is + the recommended way to create a payment token because it minimizes PCI + DSS compliance. + + Once a payment token is created, it can only be used once. + + + A payment token expires upon first use or within 30 minutes of the token + creation, whichever comes first. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CompositeToken' + description: Payment token resource. + required: true + security: + - PublishableApiKey: [] + - SecretApiKey: [] + - JWT: [] + responses: + '201': + description: Token created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CompositeToken' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $paymentCardTokenForm = new Rebilly\Entities\PaymentCardToken(); + + $paymentCardTokenForm->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + + $paymentInstrumentForm = new + Entities\PaymentInstruments\PaymentCardPaymentInstrument(); + + $paymentInstrumentForm->setPan('4111111111111111'); + + $paymentInstrumentForm->setExpYear(2025); + + $paymentInstrumentForm->setExpMonth(8); + + $paymentInstrumentForm->setCvv(123); + + + $paymentCardTokenForm->setPaymentInstrument($paymentInstrumentForm); + + + try { + $paymentCardToken = $client->paymentCardTokens()->create($paymentCardTokenForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: | + // first set the properties for the new payment token + const data = { + method: 'payment-card', + paymentInstrument: { + pan: '4111111111111111', + expYear: 2022, + expMonth: 12, + cvv: '123' + }, + billingAddress: { + firstName: 'Johnny', + lastName: 'Brown', + emails: [{ + label: 'main', + value: 'johnny+test@grr.la', + primary: true + }] + } + }; + + const token = await api.paymentTokens.create({data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $paymentCardTokenForm = new \Rebilly\Sdk\Model\PaymentCardToken(); + + $paymentCardTokenForm->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + ]); + + + $paymentInstrumentForm = new + \Rebilly\Sdk\Model\PaymentCardTokenPaymentInstrument(); + + $paymentInstrumentForm->setPan('4111111111111111'); + + $paymentInstrumentForm->setExpYear(2025); + + $paymentInstrumentForm->setExpMonth(8); + + $paymentInstrumentForm->setCvv('123'); + + + $paymentCardTokenForm->setPaymentInstrument($paymentInstrumentForm); + + + try { + $paymentCardToken = $service->paymentTokens()->create($paymentCardTokenForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + get: + x-products: + - Core + tags: + - Payment tokens + summary: Retrieve tokens + operationId: GetTokenCollection + x-sdk-operation-name: getAll + description: Retrieve a list of tokens. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: List of tokens retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CompositeToken' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $paymentCardTokens = $client->paymentCardTokens()->search([ + 'filter' => 'token:string', + ]); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.paymentTokens.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100}; + + const secondCollection = await api.paymentTokens.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(paymentToken => + console.log(paymentToken.fields.id)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $paymentCardTokensPaginator = + $service->paymentTokens()->getAllPaginator(limit: 5); + + foreach ($paymentCardTokensPaginator as $paymentCardTokensPage) { + printf("Payment card tokens page %d/%d\n", $paymentCardTokensPaginator->key() + 1, $paymentCardTokensPaginator->count()); + foreach ($paymentCardTokensPage as $paymentCardToken) { + printf("Payment card token #%s\n", $paymentCardToken->getId()); + } + } + + + // OR + + + $paymentCardTokens = $service->paymentTokens()->getAll(); + + foreach ($paymentCardTokens as $paymentCardToken) { + printf("Payment card token #%s\n", $paymentCardToken->getId()); + } + '/tokens/{token}': + parameters: + - name: token + in: path + description: ID of the token. + required: true + schema: + type: string + get: + x-products: + - Core + tags: + - Payment tokens + summary: Retrieve a token + operationId: GetToken + x-sdk-operation-name: get + description: Retrieves a token with a specified ID. + security: + - PublishableApiKey: [] + responses: + '200': + description: Token retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/CompositeToken' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $paymentCardToken = $client->paymentCardTokens()->load('tokenId'); + - lang: JavaScript + source: >- + const paymentToken = await api.paymentTokens.get({id: + 'foobar-001'}); + + console.log(paymentToken.fields.method); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $paymentCardToken = $service->paymentTokens()->get('tokenId'); + /digital-wallets/validation: + post: + x-products: + - Core + tags: + - Payment tokens + summary: Validate a digital wallet session + operationId: PostDigitalWalletValidation + x-sdk-operation-name: validate + description: >- + Validates a digital wallet session. + + + We recommend using + [FramePay](https://docs.rebilly.com/docs/developer-docs/framepay/) to + validate a digital wallet session. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DigitalWalletValidation' + description: Digital wallet validation request. + required: true + security: + - PublishableApiKey: [] + - SecretApiKey: [] + - JWT: [] + responses: + '201': + description: Digital wallet validation complete. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/DigitalWalletValidation' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /transactions: + post: + x-products: + - Core + tags: + - Transactions + summary: Create a transaction + operationId: PostTransaction + x-sdk-operation-name: create + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: >- + Creates a transaction of type `sale`, `authorize` or `setup`. + + + Use this operation for the following transactions. + + + #### Real-time decision and response + + + In this transaction, you send a request and inspect the `result` of the + response for `approved` or `declined`. + + + #### User approval/interaction required + + + In this transaction, user approval is required to complete the + transaction. + + User approval generally requires the user to interact with a third + party, + + and is common in many transactions for alternative methods. + + For example, PayPal requires user permission to complete a payment or to + accept a billing agreement. + + Payment cards may also require user approval for 3D secure + authentication. + + + If approval is required, you receive a response with a `result` value of + `unknown` and a `status` value of `waiting-approval`. + + The `_links` property of the response has a link for the `approvalUrl`. + + Open the `approvalUrl` in an iframe or in a pop. A pop is a better + workflow for mobile devices. + parameters: + - $ref: '#/components/parameters/collectionExpand' + requestBody: + $ref: '#/components/requestBodies/TransactionRequest' + responses: + '201': + description: Transaction created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Transaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $transactionForm = new Rebilly\Entities\Transaction(); + + $transactionForm->setType('sale'); + + $transactionForm->setCustomerId('customerId'); + + $transactionForm->setWebsiteId('websiteId'); + + $transactionForm->setCurrency('USD'); + + $transactionForm->setAmount(1); + + + $paymentInstruction = new + Rebilly\Entities\Transactions\PaymentInstructions\PaymentTokenInstruction(); + + $paymentInstruction->setToken('payment-token'); + + $transactionForm->setPaymentInstruction($paymentInstruction); + + + $transactionForm->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + 'dob' => '2000-06-01', + ]); + + + try { + $transaction = $client->transactions()->create($transactionForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: | + // first set the properties for the new transaction + const data = { + customerId: 'foobar-0001', + websiteId: 'my-main-website', + paymentInstrument: { + method: 'payment-card', + paymentCardId: 'my-payment-card-id', + gatewayAccountId: 'my-gateway-account-id' + }, + currency: 'USD', + amount: 12.99, + description: 'manual transaction', + }; + + const transaction = await api.transactions.create({data}); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $transactionForm = new \Rebilly\Sdk\Model\PostTransactionRequest([ + 'type' => 'sale', + 'amount' => 1, + ]); + + $transactionForm->setCustomerId('customerId'); + + $transactionForm->setWebsiteId('websiteId'); + + $transactionForm->setCurrency('USD'); + + + $paymentInstruction = new + \Rebilly\Sdk\Model\PaymentInstructionInstrument(); + + $paymentInstruction->setToken('payment-token'); + + $transactionForm->setPaymentInstruction($paymentInstruction); + + + $transactionForm->setBillingAddress([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'organization' => 'Test LTD', + 'address' => 'Test street 5', + 'address2' => 'Test house 5', + 'city' => 'New York', + 'region' => 'Long Island', + 'country' => 'US', + 'postalCode' => '123456', + 'emails' => [ + [ + 'label' => 'main', + 'value' => 'johndoe@testemail.com', + 'primary' => true, + ], + [ + 'label' => 'secondary', + 'value' => 'otheremail@testemail.com', + ], + ], + 'phoneNumbers' => [ + [ + 'label' => 'work', + 'value' => '+123456789', + 'primary' => true, + ], + [ + 'label' => 'home', + 'value' => '+9874654321', + ], + ], + 'dob' => '2000-06-01', + ]); + + + try { + $transaction = $service->transactions()->create($transactionForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + get: + x-products: + - Core + tags: + - Transactions + summary: Retrieve transactions + operationId: GetTransactionCollection + x-sdk-operation-name: getAll + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a list of transactions. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: List of transactions retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Transaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $transactions = $client->transactions()->search([ + 'filter' => 'result:approved', + ]); + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await api.transactions.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.transactions.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(transaction => + console.log(transaction.fields.type)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $transactionsPaginator = + $service->transactions()->getAllPaginator(limit: 5, filter: + 'result:approved'); + + foreach ($transactionsPaginator as $transactionsPage) { + printf("Transactions page %d/%d\n", $transactionsPaginator->key() + 1, $transactionsPaginator->count()); + foreach ($transactionsPage as $transaction) { + printf("Transaction #%s (%s): %s\n", $transaction->getId(), $transaction->getStatus(), $transaction->getDescription()); + } + } + + + // OR + + + $transactions = $service->transactions()->getAll(filter: + 'result:approved'); + + foreach ($transactions as $transaction) { + printf("Transaction #%s (%s): %s\n", $transaction->getId(), $transaction->getStatus(), $transaction->getDescription()); + } + '/transactions/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Transactions + summary: Retrieve a transaction + operationId: GetTransaction + x-sdk-operation-name: get + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a transaction with a specified ID. + parameters: + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Transaction retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Transaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $transaction = $client->transactions()->load('transactionId'); + - lang: JavaScript + source: |- + const transaction = await api.transactions.get({id: 'foobar-001'}); + console.log(transaction.fields.billingAddress.firstName); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + $transaction = $service->transactions()->get('transactionId'); + patch: + x-products: + - Core + tags: + - Transactions + summary: Update a transaction + operationId: PatchTransaction + x-sdk-operation-name: patch + description: Updates the custom fields of a transaction with a specified ID. + requestBody: + $ref: '#/components/requestBodies/PatchTransactionRequest' + responses: + '200': + description: Transaction updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Transaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + try { + $transaction = $client->transactions()->patch($transactionId, $data); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // first set the custom fields to update for the transaction + + const data = { + customFields: { + 'myCustomField': 'myCustomData', + 'myOtherField': 'myOtherData', + } + }; + + + const transaction = await api.transactions.patch({id: + 'my-transaction-id', data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\CoreService($client); + + $transactionForm = new \Rebilly\Sdk\Model\PatchTransactionRequest(); + $transactionForm->setCustomFields([ + [ + 'name' => 'customField', + 'value' => 'customValue', + ], + ]); + + try { + $transaction = $service->transactions()->patch('transactionId', $transactionForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + /payouts: + post: + x-products: + - Core + tags: + - Transactions + summary: Create a credit transaction + operationId: PostPayout + x-sdk-operation-name: create + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Creates a transaction of type `credit`. + requestBody: + $ref: '#/components/requestBodies/CreditTransactionRequest' + responses: + '201': + description: Transaction created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Transaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + '/transactions/{id}/query': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Transactions + summary: Query a transaction + operationId: PostTransactionQuery + x-sdk-operation-name: query + description: >- + Queries a transaction with a specified ID. + + + The query interacts with the related gateway account to obtain the + result, amount, and currency. + + If after analysis, the transaction must be updated, see [Update a + transaction status](../PostTransactionUpdate). + responses: + '201': + description: Transaction queried. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/TransactionQuery' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/transactions/{id}/update': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Transactions + summary: Update a transaction status + operationId: PostTransactionUpdate + x-sdk-operation-name: update + description: |- + Updates the status of a transaction with a specified ID to `completed`, + with a specified `result` and optional currency and amount values. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TransactionUpdate' + required: true + responses: + '200': + description: Transaction updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Transaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/transactions/{id}/refund': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Core + tags: + - Transactions + summary: Refund a transaction + operationId: PostTransactionRefund + x-sdk-operation-name: refund + description: |- + Refunds a transaction with a specified ID. + + The refund is in the same currency as the original transaction. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TransactionRefund' + description: Transaction resource. + required: true + responses: + '201': + description: Transaction refunded. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Transaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $transaction = $client->transactions()->refund('transactionId', + 1.99); + - lang: JavaScript + source: >- + // define the refund amount + + const data = { + amount: 12.99 + }; + + + const refund = await api.transactions.switch({id: + 'my-transaction-id', data}); + + console.log(refund.fields.status); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\CoreService($client); + + + $transactionRefund = new \Rebilly\Sdk\Model\TransactionRefund(); + + $transactionRefund->setAmount(1.99); + + + $transaction = $service->transactions()->refund('transactionId', + $transactionRefund); + '/transactions/{id}/timeline': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Transactions timeline + summary: Retrieve transaction timeline messages + operationId: GetTransactionTimelineCollection + x-sdk-operation-name: getAllTimelineMessages + description: Retrieves a list of transaction timeline messages. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of transaction timeline messages retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/TransactionTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional except for the `id` + + const firstCollection = await api.transactions + .getAllTimelineMessages({id: 'my-transaction'}); + + // alternatively you can specify one or more of them + + const params = {id: 'my-transaction', limit: 20, offset: 100}; + + const secondCollection = await + api.transactions.getAllTimelineMessages(params); + + + // access the collection items, each item is a Member + + secondCollection.items + .forEach(message => console.log(message.fields.eventType)); + post: + x-products: + - Core + tags: + - Transactions timeline + summary: Create a transaction timeline comment + operationId: PostTransactionTimeline + x-sdk-operation-name: createTimelineComment + description: Creates a transaction timeline comment. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TransactionTimeline' + description: Transaction timeline resource. + required: true + responses: + '201': + description: Transaction timeline comment created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/TransactionTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // Create a comment + + const firstComment = await api + .transactions.createTimelineComment({id: 'my-transaction-id', data: {message: 'Your comment here'}}); + + // Using params object, mentions and references + + const message = `Example of mentions @user@mydomain.com and + references #transactions-transaction-id`; + + const params = { + id: 'my-transaction-id', + data: { + message, + }, + }; + + const secondComment = await + api.transactions.createTimelineComment(params); + '/transactions/{id}/timeline/{messageId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: messageId + in: path + description: ID of the transaction timeline message. + required: true + schema: + type: string + get: + x-products: + - Core + tags: + - Transactions timeline + summary: Retrieve a transaction Timeline message + operationId: GetTransactionTimeline + x-sdk-operation-name: getTimelineMessage + description: Retrieves a timeline message with a specified ID. + responses: + '200': + description: Timeline message retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/TransactionTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const message = await api.transactions + .getTimelineMessage({id: 'foobar-001', messageId: 'message-202'}); + console.log(message.fields.eventType); + delete: + x-products: + - Core + tags: + - Transactions timeline + summary: Delete a transaction timeline message + operationId: DeleteTransactionTimeline + x-sdk-operation-name: deleteTimelineMessage + description: Deletes a transaction timeline message with a specified ID. + responses: + '204': + description: Transaction timeline message deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: JavaScript + source: |- + const request = await api.transactions + .deleteTimelineMessage({id: 'foobar-001', messageId: 'message-202'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + /fees: + get: + x-badge: Experimental + x-products: + - Core + tags: + - Fees + summary: Retrieve fee entries + description: >- + Retrieves a list of fee entries. + + + > **Important:** This operation is experimental and may not be backward + compatible. + operationId: GetFeeCollection + x-sdk-operation-name: getAll + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of fee entries retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Fee' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-badge: Experimental + x-products: + - Core + tags: + - Fees + summary: Create a fee entry + description: >- + Creates a fee entry. + + + > **Important:** This operation is experimental and may not be backward + compatible. + operationId: PostFee + x-sdk-operation-name: create + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Fee' + examples: + stripe-trx: + $ref: '#/components/examples/stripe-trx-request' + stripe-us: + $ref: '#/components/examples/stripe-us-request' + stripe-intl: + $ref: '#/components/examples/stripe-intl-request' + responses: + '201': + description: Fee entry created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Fee' + examples: + stripe-trx: + $ref: '#/components/examples/stripe-trx-response' + stripe-us: + $ref: '#/components/examples/stripe-us-response' + stripe-intl: + $ref: '#/components/examples/stripe-intl-response' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/fees/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-badge: Experimental + x-products: + - Core + tags: + - Fees + summary: Retrieve a fee entry + description: >- + Retrieves a fee entry. + + + > **Important:** This operation is experimental and may not be backward + compatible. + operationId: GetFee + x-sdk-operation-name: get + responses: + '200': + description: Fee retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Fee' + examples: + stripe-trx: + $ref: '#/components/examples/stripe-trx-response' + stripe-us: + $ref: '#/components/examples/stripe-us-response' + stripe-intl: + $ref: '#/components/examples/stripe-intl-response' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-badge: Experimental + x-products: + - Core + tags: + - Fees + summary: Upsert a fee entry + description: >- + Creates or updates (upserts) a fee entry. + + + > **Important:** This operation is experimental and may not be backward + compatible. + operationId: PutFee + x-sdk-operation-name: upsert + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Fee' + examples: + stripe-trx: + $ref: '#/components/examples/stripe-trx-request' + stripe-us: + $ref: '#/components/examples/stripe-us-request' + stripe-intl: + $ref: '#/components/examples/stripe-intl-request' + responses: + '200': + description: Fee entry updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Fee' + examples: + stripe-trx: + $ref: '#/components/examples/stripe-trx-response' + stripe-us: + $ref: '#/components/examples/stripe-us-response' + stripe-intl: + $ref: '#/components/examples/stripe-intl-response' + '201': + description: Fee entry created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Fee' + examples: + stripe-trx: + $ref: '#/components/examples/stripe-trx-response' + stripe-us: + $ref: '#/components/examples/stripe-us-response' + stripe-intl: + $ref: '#/components/examples/stripe-intl-response' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + patch: + x-badge: Experimental + x-products: + - Core + tags: + - Fees + summary: Patch a fee entry + description: >- + Patches a fee entry. + + + > **Important:** This operation is experimental and may not be backward + compatible. + operationId: PatchFee + x-sdk-operation-name: patch + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PatchFee' + examples: + stripe-trx: + $ref: '#/components/examples/stripe-trx-request' + stripe-us: + $ref: '#/components/examples/stripe-us-request' + stripe-intl: + $ref: '#/components/examples/stripe-intl-request' + responses: + '200': + description: Fee entry patched. + content: + application/json: + schema: + $ref: '#/components/schemas/Fee' + examples: + stripe-trx: + $ref: '#/components/examples/stripe-trx-response' + stripe-us: + $ref: '#/components/examples/stripe-us-response' + stripe-intl: + $ref: '#/components/examples/stripe-intl-response' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-badge: Experimental + x-products: + - Core + tags: + - Fees + summary: Delete a fee entry + description: >- + Deletes a fee entry. + + + > **Important:** This operation is experimental and may not be backward + compatible. + operationId: DeleteFee + x-sdk-operation-name: delete + responses: + '204': + description: Fee deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /api-keys: + get: + x-products: + - Users + tags: + - API keys + summary: Retrieve API keys + operationId: GetApiKeyCollection + x-sdk-operation-name: getAll + description: Retrieve a list of API keys. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of API keys retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiKey' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $apiKeys = $client->apiKeys()->search([ + 'filter' => 'description:Test', + ]); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.apiKeys.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.apiKeys.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(apiKey => + console.log(apiKey.fields.description)); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + + $apiKeysPaginator = $service->apiKeys()->getAllPaginator(limit: 5); + foreach ($apiKeysPaginator as $apiKeysPage) { + printf("ApiKeys page %d/%d\n", $apiKeysPaginator->key() + 1, $apiKeysPaginator->count()); + foreach ($apiKeysPage as $apiKey) { + printf("ApiKey #%s: %s\n", $apiKey->getId(), $apiKey->getDescription()); + } + } + + // OR + + $apiKeys = $service->apiKeys()->getAll(); + foreach ($apiKeys as $apiKey) { + printf("ApiKey #%s: %s\n", $apiKey->getId(), $apiKey->getDescription()); + } + post: + x-products: + - Users + tags: + - API keys + summary: Create an API key + operationId: PostApiKey + x-sdk-operation-name: create + description: Create an API key. + requestBody: + $ref: '#/components/requestBodies/ApiKey' + responses: + '201': + description: API key created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ApiKey' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $apiKeyForm = new Rebilly\Entities\ApiKey(); + $apiKeyForm->setDescription('Test key'); + + try { + $apiKey = $client->apiKeys()->create($apiKeyForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the properties for the new API key + + const data = { + description: 'My new API key', + type: 'secret' + }; + + + // the ID is optional + + const firstKey = await api.apiKeys.create({data}); + + + // or you can provide one + + const secondKey = await api.apiKeys.create({id: 'my-second-key', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + + $apiKeyForm = new \Rebilly\Sdk\Model\ApiKey(); + $apiKeyForm->setDescription('Test key'); + + try { + $apiKey = $service->apiKeys()->create($apiKeyForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/api-keys/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - API keys + summary: Retrieve API key + operationId: GetApiKey + x-sdk-operation-name: get + description: Retrieve API key with a specified ID. + responses: + '200': + description: API key retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ApiKey' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $apiKeys = $client->apiKeys()->load('apiKeyID'); + - lang: JavaScript + source: |- + const apiKey = await api.apiKeys.get({id: 'foobar-001'}); + console.log(apiKey.fields.description); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $apiKeys = $service->apiKeys()->get('apiKeyID'); + put: + x-products: + - Users + tags: + - API keys + summary: Upsert an API key + operationId: PutApiKey + x-sdk-operation-name: update + description: Create or update (upsert) API key with a specified ID. + responses: + '200': + description: API key updated. + content: + application/json: + schema: + $ref: '#/components/schemas/ApiKey' + '201': + description: API key created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ApiKey' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + $ref: '#/components/requestBodies/ApiKey' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $apiKeyForm = new Rebilly\Entities\ApiKey(); + $apiKeyForm->setDescription('Test key'); + + try { + $apiKey = $client->apiKeys()->update('apiKeyID', $apiKeyForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // define values to update + + const data = { + description: 'A better description' + }; + + + const apiKey = await api.apiKeys.update({id: 'my-second-key', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + + $apiKeyForm = new \Rebilly\Sdk\Model\ApiKey(); + $apiKeyForm->setDescription('Test key'); + + try { + $apiKey = $service->apiKeys()->update('apiKeyID', $apiKeyForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Users + tags: + - API keys + summary: Delete API key + operationId: DeleteApiKey + x-sdk-operation-name: delete + description: Deletes an API key with a specified ID. + responses: + '204': + description: API key deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->apiKeys()->delete('apiKeyID'); + - lang: JavaScript + source: |- + const request = await api.apiKeys.delete({id: 'my-second-key'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $service->apiKeys()->delete('apiKeyID'); + /balance-transactions: + get: + x-badge: Experimental + x-products: + - Users + tags: + - Balance transactions + summary: Retrieve balance transactions + description: |- + Retrieves a list of balance transactions. + + > **Important:** This operation is experimental and may change. + operationId: GetBalanceTransactionCollection + x-sdk-operation-name: getAll + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of balance transactions retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/BalanceTransaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/balance-transactions/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-badge: Experimental + x-products: + - Users + tags: + - Balance transactions + summary: Retrieve a balance transaction + description: |- + Retrieves a balance transaction with a specified identifier. + + > **Important:** This operation is experimental and may change. + operationId: GetBalanceTransaction + x-sdk-operation-name: get + responses: + '200': + description: Balance transaction retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/BalanceTransaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /billing-portals: + get: + x-products: + - Users + tags: + - Billing portals + summary: Retrieve billing portals + operationId: GetBillingPortalCollection + x-sdk-operation-name: getAll + description: Retrieves a list of billing portals. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of billing portals retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/BillingPortal' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Users + tags: + - Billing portals + summary: Create a billing portal + operationId: PostBillingPortal + x-sdk-operation-name: create + description: Creates a billing portal. + responses: + '201': + description: Billing portal created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/BillingPortal' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BillingPortal' + '/billing-portals/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Billing portals + summary: Retrieve a billing portal + operationId: GetBillingPortal + x-sdk-operation-name: get + description: Retrieves a billing portal with a specified ID. + responses: + '200': + description: Billing portal retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/BillingPortal' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Users + tags: + - Billing portals + summary: Upsert a billing portal + operationId: PutBillingPortal + x-sdk-operation-name: update + description: Creates or updates (upserts) a billing portal with a specified ID. + responses: + '200': + description: Billing portal updated. + content: + application/json: + schema: + $ref: '#/components/schemas/BillingPortal' + '201': + description: Billing portal created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/BillingPortal' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BillingPortal' + delete: + x-products: + - Users + tags: + - Billing portals + summary: Delete a billing portal + operationId: DeleteBillingPortal + x-sdk-operation-name: delete + description: Deletes a billing portal with a specified ID. + responses: + '204': + description: Billing portal deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /broadcast-messages: + get: + x-products: + - Users + tags: + - Broadcast messages + summary: Retrieve broadcast messages + operationId: GetBroadcastMessageCollection + x-sdk-operation-name: getAll + description: Retrieves a list of broadcast messages. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: List of broadcast messages retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/BroadcastMessage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Users + tags: + - Broadcast messages + summary: Create a broadcast message + operationId: PostBroadcastMessage + x-sdk-operation-name: create + description: Creates a broadcast message. + responses: + '201': + description: Broadcast message created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/BroadcastMessage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BroadcastMessage' + description: Broadcast message resource. + required: true + '/broadcast-messages/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Broadcast messages + summary: Retrieve a broadcast message + operationId: GetBroadcastMessage + x-sdk-operation-name: get + description: Retrieves a broadcast message. + responses: + '200': + description: Broadcast message retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/BroadcastMessage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Users + tags: + - Broadcast messages + summary: Delete a broadcast message + operationId: DeleteBroadcastMessage + x-sdk-operation-name: delete + description: Deletes a broadcast message with a specified ID. + responses: + '204': + description: Broadcast message deleted. + patch: + x-products: + - Users + tags: + - Broadcast messages + summary: Update a broadcast message + operationId: PatchBroadcastMessage + x-sdk-operation-name: update + description: Updates a broadcast message with a specified ID. + responses: + '200': + description: Broadcast message updated. + content: + application/json: + schema: + $ref: '#/components/schemas/BroadcastMessage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BroadcastMessage' + description: Broadcast message resource. + required: true + /deposit-requests: + post: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Create a deposit request + operationId: PostDepositRequest + x-sdk-operation-name: create + description: >- + Creates a deposit request. + + To complete the deposit, the customer is redirected to the `deposit` + link. + + After the deposit, the customer is redirected to the `redirectUrl`. + + Corresponding transaction webhooks are sent to webhooks subscribers. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PostDepositRequest' + description: Deposit request resource. + required: true + responses: + '201': + description: Deposit request created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/DepositRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + get: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Retrieve deposit requests + operationId: GetDepositRequestCollection + x-sdk-operation-name: getAll + description: Retrieves a list of deposit requests. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of deposit requests retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DepositRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/deposit-requests/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Retrieve a deposit request + operationId: GetDepositRequest + x-sdk-operation-name: get + description: Retrieves a deposit request with a specified ID. + parameters: + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Deposit request retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/DepositRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /deposit-strategies: + post: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Create a deposit strategy + operationId: PostDepositStrategy + x-sdk-operation-name: create + description: >- + Creates a deposit strategy. + + To complete the deposit, the customer is redirected to the `deposit` + link. + + After the deposit, the customer is redirected to the `redirectUrl`. + + Corresponding transaction webhooks are sent to webhooks subscribers. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DepositStrategy' + description: Deposit strategy resource. + required: true + responses: + '201': + description: Deposit strategy created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/DepositStrategy' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + get: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Retrieve deposit strategies + operationId: GetDepositStrategyCollection + x-sdk-operation-name: getAll + description: Retrieves a list of deposit strategies. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of deposit strategies retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DepositStrategy' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/deposit-strategies/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Retrieve a deposit strategy + operationId: GetDepositStrategy + x-sdk-operation-name: get + description: Retrieves a deposit strategy with a specified ID. + responses: + '200': + description: Deposit strategy retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/DepositStrategy' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Upsert a deposit strategy + operationId: PutDepositStrategy + x-sdk-operation-name: update + description: Creates or updates (upserts) a deposit strategy with a specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DepositStrategy' + description: Deposit strategy resource. + required: true + responses: + '200': + description: Deposit strategy updated. + content: + application/json: + schema: + $ref: '#/components/schemas/DepositStrategy' + '201': + description: Deposit strategy created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/DepositStrategy' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Delete a deposit strategy + operationId: DeleteDepositStrategy + x-sdk-operation-name: delete + description: Deletes a deposit strategy with a specified ID. + responses: + '204': + description: Deposit strategy deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /deposit-custom-property-sets: + post: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Create a custom deposit property set + operationId: PostDepositCustomPropertySet + x-sdk-operation-name: create + description: >- + Creates a custom deposit property set. + + A custom property set is a JSON-schema to extend the request form with + extra form fields. + + The collected information is stored at the deposit request. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DepositCustomPropertySet' + description: Custom deposit property set resource. + required: true + responses: + '201': + description: Custom deposit property set created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/DepositCustomPropertySet' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + get: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Retrieve custom deposit properties sets + operationId: GetDepositCustomPropertySetCollection + x-sdk-operation-name: getAll + description: Retrieves a list of custom deposit property sets. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of custom deposit property sets retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DepositCustomPropertySet' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/deposit-custom-property-sets/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Retrieve a custom deposit property set + operationId: GetDepositCustomPropertySet + x-sdk-operation-name: get + description: Retrieves a custom deposit property set with a specified ID. + responses: + '200': + description: Custom deposit property set retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/DepositCustomPropertySet' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Upsert a custom deposit property set + operationId: PutDepositCustomPropertySet + x-sdk-operation-name: update + description: >- + Creates or updates (upserts) a custom deposit property set with a + specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DepositCustomPropertySet' + description: Custom deposit property set resource. + required: true + responses: + '200': + description: Custom deposit property updated. + content: + application/json: + schema: + $ref: '#/components/schemas/DepositCustomPropertySet' + '201': + description: Custom deposit property set created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/DepositCustomPropertySet' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Core + x-badge: Experimental + tags: + - Deposits + summary: Delete a custom deposit property set + operationId: DeleteDepositCustomPropertySet + x-sdk-operation-name: delete + description: Deletes a custom deposit property set with a specified ID. + responses: + '204': + description: Custom deposit property set deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /checkout-forms: + get: + x-products: + - Users + tags: + - Checkout forms + summary: Retrieve checkout forms + operationId: GetCheckoutFormCollection + x-sdk-operation-name: getAll + description: Retrieves a list of checkout forms. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: Checkout forms retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CheckoutForm' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Users + tags: + - Checkout forms + summary: Create a checkout form + operationId: PostCheckoutForm + x-sdk-operation-name: create + description: Creates a checkout form. + responses: + '201': + description: Checkout form created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CheckoutForm' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CheckoutForm' + '/checkout-forms/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Checkout forms + summary: Retrieve a checkout form + operationId: GetCheckoutForm + x-sdk-operation-name: get + description: Retrieves a checkout form with a specified ID. + responses: + '200': + description: Checkout form retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/CheckoutForm' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Users + tags: + - Checkout forms + summary: Upsert a checkout form + operationId: PutCheckoutForm + x-sdk-operation-name: update + description: Creates or updates (upserts) a checkout form with a specified ID. + responses: + '200': + description: Checkout form updated. + content: + application/json: + schema: + $ref: '#/components/schemas/CheckoutForm' + '201': + description: Checkout form created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CheckoutForm' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CheckoutForm' + delete: + x-products: + - Users + tags: + - Checkout forms + summary: Delete a checkout form + operationId: DeleteCheckoutForm + x-sdk-operation-name: delete + description: Deletes a checkout form with a specified ID. + responses: + '204': + description: Checkout form deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/service-credentials/{type}': + parameters: + - $ref: '#/components/parameters/credentialType' + get: + x-products: + - Users + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + tags: + - Service credentials + summary: Retrieve service credentials + operationId: GetServiceCredentialCollection + x-sdk-operation-name: getAll + description: Retrieves a list of service credentials. + responses: + '200': + description: List of service credentials retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ServiceCredential' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: > + // type is required, other parameters are optional + + const firstCollection = await api.serviceCredentials.getAll({type: + 'webhook'}); + + + // alternatively you can specify one or more of them + + const params = {type: 'webhook', limit: 20, offset: 100}; + + const secondCollection = await + api.serviceCredentials.getAll(params); + + + // access the collection items, each item is a ServiceCredential + + secondCollection.items.forEach(serviceCredential => + console.log(serviceCredential.fields.status)); + post: + x-products: + - Users + tags: + - Service credentials + summary: Create a service credential + operationId: PostServiceCredential + x-sdk-operation-name: create + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Creates a service credential. + responses: + '201': + description: Service credential created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceCredential' + '303': + description: Existing service credential retrieved. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceCredential' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + $ref: '#/components/requestBodies/PostServiceCredential' + x-codeSamples: + - lang: JavaScript + source: > + // first set the required properties for the new service credential + + const data = { + host: 'foobar.test.com', + auth: { + type: 'none' + } + }; + + + const credential = await api.serviceCredentials.create({type: + 'webhook', data}); + + // use the ID to authenticate your webhook in Rebilly + + console.log(credential.fields.id); + '/service-credentials/{type}/{id}': + parameters: + - $ref: '#/components/parameters/credentialType' + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Service credentials + summary: Retrieve a service credential + operationId: GetServiceCredential + x-sdk-operation-name: get + description: Retrieves a service credential with a specified ID. + responses: + '200': + description: Service credential retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceCredential' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: > + const credential = await api.serviceCredentials.get({type: + 'webhook', id: 'service-credential-1'}); + + console.log(credential.fields.status); + patch: + x-products: + - Users + tags: + - Service credentials + summary: Update a service credential + operationId: PatchServiceCredential + x-sdk-operation-name: update + description: Updates a service credential with a specified ID. + requestBody: + $ref: '#/components/requestBodies/PatchCredential' + responses: + '200': + description: Service credential updated. + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceCredential' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: > + // first set the status to update for the service credential + + const data = { + status: 'deactivated' + }; + + + const credential = await api.serviceCredentials.update({type: + 'webhook', id: 'service-credential-1', data}); + '/service-credentials/{type}/{id}/items': + parameters: + - $ref: '#/components/parameters/credentialType' + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Service credentials + summary: List credential root items + operationId: GetServiceCredentialItemCollection + x-sdk-operation-name: getItems + description: Retrieves a list of root items for a specified credential. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionFields' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of credential root items retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/GoogleSpreadsheet' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: > + // type and id are required, other parameters are optional + + const firstCollection = await api.serviceCredentials.getItems({type: + 'oauth2', id: 'service-credential-1'}); + + + // alternatively you can specify one or more of them + + const params = {type: 'oauth2', id: 'service-credential-1', limit: + 20, offset: 100}; + + const secondCollection = await + api.serviceCredentials.getItems(params); + /custom-domains: + get: + x-products: + - Users + tags: + - Custom domains + summary: Retrieve custom domains + operationId: GetCustomDomainCollection + x-sdk-operation-name: getAll + description: Retrieve a list of custom domains. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of custom domains retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CustomDomain' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Users + tags: + - Custom domains + summary: Create a custom domain + operationId: PostCustomDomain + x-sdk-operation-name: create + description: Create a custom domain. + responses: + '201': + description: Custom domain created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/CustomDomain' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CustomDomain' + '/custom-domains/{domain}': + parameters: + - name: domain + in: path + description: Resource domain. + required: true + schema: + type: string + maxLength: 255 + pattern: '^[@~\-\.\w]+$' + get: + x-products: + - Users + tags: + - Custom domains + summary: Retrieve a custom domain + operationId: GetCustomDomain + x-sdk-operation-name: get + description: Retrieve a custom domain with a specified ID. + responses: + '200': + description: Custom domain retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/CustomDomain' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Users + tags: + - Custom domains + summary: Delete a custom domain + operationId: DeleteCustomDomain + x-sdk-operation-name: delete + description: Delete a custom domain with a specified ID. + responses: + '204': + description: Custom domains deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/email-delivery-setting-verifications/{token}': + parameters: + - $ref: '#/components/parameters/token' + put: + x-products: + - Users + tags: + - Email delivery settings + summary: Verify an email delivery setting + operationId: PutEmailDeliverySettingsVerification + x-sdk-operation-name: verify + description: Verifies an email delivery setting. + responses: + '200': + description: Email delivery setting verified. + content: + application/json: + schema: + $ref: '#/components/schemas/EmailDeliverySetting' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /email-delivery-settings: + get: + x-products: + - Users + tags: + - Email delivery settings + summary: Retrieve email delivery settings + operationId: GetEmailDeliverySettingCollection + x-sdk-operation-name: getAll + description: Retrieves email delivery settings. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of email delivery settings retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/EmailDeliverySetting' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Users + tags: + - Email delivery settings + summary: Create an email delivery setting + operationId: PostEmailDeliverySetting + x-sdk-operation-name: create + description: |- + Creates an email delivery setting. + Email delivery settings are used together with email messages, + or templates, to send emails. + responses: + '201': + description: Email delivery setting created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/EmailDeliverySetting' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EmailDeliverySetting' + description: Email delivery setting resource. + required: true + '/email-delivery-settings/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Email delivery settings + summary: Retrieve an email delivery setting + operationId: GetEmailDeliverySetting + x-sdk-operation-name: get + description: Retrieves an email delivery setting with a specified ID. + responses: + '200': + description: Email delivery setting retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/EmailDeliverySetting' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Users + tags: + - Email delivery settings + summary: Delete an email delivery setting + operationId: DeleteEmailDeliverySetting + x-sdk-operation-name: delete + description: Deletes an email delivery setting with a specified ID. + responses: + '204': + description: Email delivery setting deleted. + '409': + $ref: '#/components/responses/Conflict' + patch: + x-products: + - Users + tags: + - Email delivery settings + summary: Update an email delivery setting + operationId: PatchEmailDeliverySetting + x-sdk-operation-name: update + description: Updates an email delivery setting with a specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EmailDeliverySetting' + description: Email delivery setting resource. + required: true + responses: + '200': + description: Email delivery setting updated. + content: + application/json: + schema: + $ref: '#/components/schemas/EmailDeliverySetting' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + '/email-delivery-settings/{id}/resend-email-verification': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Users + tags: + - Email delivery settings + summary: Resend email delivery setting verification email + operationId: PostResendEmailDeliverySettingVerification + x-sdk-operation-name: resendVerification + description: >- + Resends a verification email for an email delivery setting with a + specified ID. + responses: + '200': + description: Verification email sent. + content: + application/json: + schema: + $ref: '#/components/schemas/EmailDeliverySetting' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + /email-messages: + get: + x-products: + - Users + tags: + - Email messages + summary: Retrieve email messages + operationId: GetEmailMessageCollection + x-sdk-operation-name: getAll + description: Retrieves a list of email messages. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: List of email messages retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/EmailMessage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Users + tags: + - Email messages + summary: Create an email message + operationId: PostEmailMessage + x-sdk-operation-name: create + description: Creates an email message. + responses: + '201': + description: Email message created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/EmailMessage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EmailMessage' + description: Email message resource. + required: true + '/email-messages/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Email messages + summary: Retrieve an email message + operationId: GetEmailMessage + x-sdk-operation-name: get + description: Retrieves an email message with a specified ID. + responses: + '200': + description: Email message retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/EmailMessage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Users + tags: + - Email messages + summary: Delete an email message + operationId: DeleteEmailMessage + x-sdk-operation-name: delete + description: Deletes an email message with a specified ID. + responses: + '204': + description: Email message deleted. + '409': + $ref: '#/components/responses/Conflict' + patch: + x-products: + - Users + tags: + - Email messages + summary: Send an email message + operationId: PatchEmailMessage + x-sdk-operation-name: send + description: Sends an email message. + responses: + '200': + description: Email message accepted. + content: + application/json: + schema: + $ref: '#/components/schemas/EmailMessage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + type: object + title: PatchEmailMessageRequest + required: + - status + properties: + status: + description: Status of the email message. + type: string + enum: + - outbox + description: Email message status. + required: true + /email-notifications: + get: + x-products: + - Users + tags: + - Email notifications + summary: Retrieve email notification events + operationId: GetEmailNotificationCollection + x-sdk-operation-name: getAll + description: Retrieves a list email notification events. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: List of email notification events retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/EmailNotification' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + /events: + get: + x-products: + - Users + tags: + - Rules + summary: Retrieve system events + operationId: GetEventCollection + x-sdk-operation-name: getAll + description: Retrieves a list of system events. + responses: + '200': + description: List of system events retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SystemEvent' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.events.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100}; + + const secondCollection = await api.events.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(event => + console.log(event.fields.eventType)); + '/events/{eventType}': + parameters: + - $ref: '#/components/parameters/systemEventType' + get: + x-products: + - Users + tags: + - Rules + summary: Retrieve event information + operationId: GetEvent + x-sdk-operation-name: get + description: Retrieves event information for a specified event type. + responses: + '200': + description: Rules retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/SystemEvent' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + const eventDetails = await api.events.get({eventType: + 'gateway-account-created'}); + + console.log(eventDetails.fields.rulesCount); + '/events/{eventType}/rules': + parameters: + - $ref: '#/components/parameters/systemEventType' + get: + x-products: + - Users + tags: + - Rules + summary: Retrieve event rules + operationId: GetEventRuleCollection + x-sdk-operation-name: getRules + description: Retrieves rules associated with a specified event type. + responses: + '200': + description: Rules retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/RuleSet' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + const event = await api.events.getRules({eventType: + 'transaction-processed'}); + + console.log(event.fields.rules); + put: + x-products: + - Users + tags: + - Rules + summary: Update event rules + operationId: PutEventRule + x-sdk-operation-name: createRules + description: Updates rules associated with a specified event type. + responses: + '200': + description: Rules updated. + content: + application/json: + schema: + $ref: '#/components/schemas/RuleSet' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + $ref: '#/components/requestBodies/RuleSet' + x-codeSamples: + - lang: JavaScript + source: > + // define at least one rule + + const data = { + rules: [ + { + name: 'The One Rule', + status: 'active', + final: true, + criteria: {}, + actions: [ + { + name: 'blocklist', + status: 'active', + type: 'email', + ttl: 789 + }, + { + name: 'stop-subscriptions', + status: 'active' + } + ] + } + + ] + }; + + + const ruleset = await api.events.createRules({eventType: + 'risk-score-changed', data}); + + console.log(ruleset.fields.version); + + + + + // updating rules + + + // define the ruleset to override the current values within + + // the event + + const data = { + rules: [ + { + name: 'The One Rule', + status: 'active', + final: true, + criteria: {}, + actions: [ + { + name: 'blocklist', + status: 'active', + type: 'email', + ttl: 789 + }, + { + name: 'stop-subscriptions', + status: 'active' + } + ] + } + + ] + }; + + + const ruleset = await api.events.updateRules({eventType: + 'risk-score-changed', data}); + + // each time the event's ruleset is modified + + // the version number is incremented + + console.log(ruleset.fields.version); + '/events/{eventType}/timeline': + parameters: + - $ref: '#/components/parameters/systemEventType' + get: + x-products: + - Users + tags: + - Rules timeline + summary: Retrieve rules engine timeline messages + operationId: GetRuleSetCollection + x-sdk-operation-name: getAllTimelineMessages + description: Retrieves a list of rules engine timeline messages. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of rules engine timeline messages retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/RulesEngineTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Users + tags: + - Rules timeline + summary: Create a rules engine timeline comment + operationId: PostRuleSetTimeline + x-sdk-operation-name: createTimelineComment + description: Creates a rules engine timeline comment. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RulesEngineTimeline' + description: Rules engine timeline resource. + required: true + responses: + '201': + description: Rules engine timeline comment created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/RulesEngineTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/events/{eventType}/timeline/{messageId}': + parameters: + - $ref: '#/components/parameters/systemEventType' + - name: messageId + in: path + description: ID of the rules engine timeline message. + required: true + schema: + type: string + get: + x-products: + - Users + tags: + - Rules timeline + summary: Retrieve a rules engine timeline message + operationId: GetRuleSetTimeline + x-sdk-operation-name: getTimelineMessage + description: Retrieves a rules engine timeline message with a specified ID. + responses: + '200': + description: Rules engine message retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/RulesEngineTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Users + tags: + - Rules timeline + summary: Delete a rules engine timeline message + operationId: DeleteRuleSetTimeline + x-sdk-operation-name: deleteTimelineMessage + description: Deletes a rules engine timeline message with a specified ID. + responses: + '204': + description: Rules engine timeline message deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '/events/{eventType}/rules/history': + parameters: + - $ref: '#/components/parameters/systemEventType' + get: + x-products: + - Users + tags: + - Rules + summary: Retrieve change history of a ruleset + operationId: GetEventRuleHistoryCollection + x-sdk-operation-name: getRulesHistory + description: |- + Retrieves the change history of a ruleset with a specified event type. + All ruleset changes are recorded in the change history. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFields' + responses: + '200': + description: Ruleset history retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/RuleSetHistoryItem' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + // get the latest 20 versions for this event ID + + const history = await api.events.getRulesVersionNumber({eventType: + 'risk-score-changed', limit: 20}); + + // each item exposes the version and `createdTime` + + history.items.forEach(edit => console.log(edit.fields.version)); + '/events/{eventType}/rules/history/{version}': + parameters: + - $ref: '#/components/parameters/systemEventType' + - $ref: '#/components/parameters/rulesVersion' + get: + x-products: + - Users + tags: + - Rules + summary: Retrieve change history record of a ruleset + operationId: GetEventRuleHistory + x-sdk-operation-name: getRulesVersionNumber + description: >- + Retrieves the change history record of a ruleset with a specified event + type. + + A history record is created each time rules are changed. + parameters: + - $ref: '#/components/parameters/collectionFields' + responses: + '200': + description: Ruleset history record retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/RuleSetHistoryItem' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + // get version #2 details for this event ID + + const history = await api.events.getRulesVersionNumber({eventType: + 'risk-score-changed', version: 2}); + + // the history exposes the version number and its `createdTime` + + console.log(history.fields.createdTime); + '/events/{eventType}/rules/versions/{version}': + parameters: + - $ref: '#/components/parameters/systemEventType' + - $ref: '#/components/parameters/rulesVersion' + get: + x-products: + - Users + tags: + - Rules + summary: Retrieve a version of a ruleset + operationId: GetEventRuleVersion + x-sdk-operation-name: getRulesVersionDetail + description: |- + Retrieves a version of a ruleset with a specified event type. + A new version is created each time you change a rule. + parameters: + - $ref: '#/components/parameters/collectionFields' + responses: + '200': + description: Ruleset version retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/RuleSetVersion' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + // get version #2 for this event ID + + const version = await api.events.getRulesVersionDetail({eventType: + 'risk-score-changed', version: 2}); + + // the version exposes the ruleset + + console.log(version.fields.rules); + '/events/{eventType}/rules/drafts': + parameters: + - $ref: '#/components/parameters/systemEventType' + get: + x-products: + - Users + tags: + - Rules + summary: Retrieve draft rulesets + operationId: GetEventRuleSetDraftCollection + x-sdk-operation-name: getAllDraftRulesets + description: |- + Retrieves a list of draft rulesets for a specified event type. + The result are sorted by updated time in descending order. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFields' + responses: + '200': + description: Draft rulesets retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/RuleSetDraft' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + post: + x-products: + - Users + tags: + - Rules + summary: Create a draft ruleset + operationId: PostEventRuleSetDraft + x-sdk-operation-name: createDraftRuleset + description: Creates a draft ruleset for a specified event type. + responses: + '201': + description: Draft ruleset created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/RuleSetDraft' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + $ref: '#/components/requestBodies/RuleSetDraft' + '/events/{eventType}/rules/drafts/{id}': + parameters: + - $ref: '#/components/parameters/systemEventType' + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Rules + summary: Retrieve a draft ruleset + operationId: GetEventRuleSetDraft + x-sdk-operation-name: getDraftRuleset + description: Retrieves a draft ruleset with a specified ID and event type. + parameters: + - $ref: '#/components/parameters/collectionFields' + responses: + '200': + description: Draft ruleset retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/RuleSetDraft' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Users + tags: + - Rules + summary: Delete a draft ruleset + operationId: DeleteEventRuleSetDraft + x-sdk-operation-name: deleteDraftRuleset + description: Deletes a draft ruleset with a specified ID and event type. + responses: + '204': + description: Draft ruleset deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Users + tags: + - Rules + summary: Update a draft ruleset + operationId: PutEventRuleSetDraft + x-sdk-operation-name: updateDraftRuleset + description: Updates a draft ruleset for a specified event type. + responses: + '200': + description: Draft ruleset updated. + content: + application/json: + schema: + $ref: '#/components/schemas/RuleSetDraft' + '201': + description: Draft ruleset created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/RuleSetDraft' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + $ref: '#/components/requestBodies/RuleSetDraft' + /forgot-password: + servers: + - url: 'https://api-sandbox.rebilly.com' + description: Sandbox server + - url: 'https://api.rebilly.com' + description: Live server + post: + x-products: + - Users + tags: + - Reset password + summary: Request a password reset + operationId: PostForgotPasswordRequest + x-sdk-operation-name: forgotPassword + description: Sends an email with a link containing a token to reset a user password. + security: [] + responses: + '204': + description: Email sent. + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ForgotPassword' + description: Forgot password resource. + required: true + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $forgotPasswordForm = new Rebilly\Entities\ForgotPassword(); + $forgotPasswordForm->setEmail('johndoe@test.com'); + + try { + $client->users()->forgotPassword($forgotPasswordForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: |- + const data = { + email: 'acme+test@rebilly.com' + }; + + const request = await api.account.forgotPassword({data}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + + $forgotPasswordForm = new \Rebilly\Sdk\Model\ForgotPassword(); + $forgotPasswordForm->setEmail('johndoe@test.com'); + + try { + $service->account()->forgotPassword($forgotPasswordForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + /gateway-accounts: + get: + x-products: + - Users + tags: + - Gateway accounts + summary: Retrieve gateway accounts + operationId: GetGatewayAccountCollection + x-sdk-operation-name: getAll + description: Retrieves a list of gateway accounts. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionFields' + responses: + '200': + description: List of gateway accounts retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/GatewayAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $gatewayAccounts = $client->$gatewayAccounts()->search([ + 'filter' => 'currency:USD', + ]); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.gatewayAccounts.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.gatewayAccounts.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(gatewayAccount => + console.log(gatewayAccount.fields.gatewayName)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + + $gatewayAccountsPaginator = + $service->gatewayAccounts()->getAllPaginator(limit: 5, filter: + 'currency:USD'); + + foreach ($gatewayAccountsPaginator as $gatewayAccountsPage) { + printf("Gateway accounts page %d/%d\n", $gatewayAccountsPaginator->key() + 1, $gatewayAccountsPaginator->count()); + foreach ($gatewayAccountsPage as $gatewayAccount) { + printf("Gateway account #%s: %s\n", $gatewayAccount->getId(), $gatewayAccount->getGatewayName()); + } + } + + + // OR + + + $gatewayAccounts = $service->gatewayAccounts()->getAll(filter: + 'currency:USD'); + + foreach ($gatewayAccounts as $gatewayAccount) { + printf("Gateway account #%s: %s\n", $gatewayAccount->getId(), $gatewayAccount->getGatewayName()); + } + post: + x-products: + - Users + tags: + - Gateway accounts + summary: Create a gateway account + operationId: PostGatewayAccount + x-sdk-operation-name: create + description: Creates a gateway account. + requestBody: + $ref: '#/components/requestBodies/GatewayAccount' + responses: + '201': + description: Gateway account created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + description: Invalid data sent. + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $gatewayAccountForm = new Rebilly\Entities\GatewayAccount(); + + + $gatewayAccountForm->setGatewayName('A1Gateway'); + + $gatewayAccountForm->setAcquirerName('Bank of Rebilly'); + + $gatewayAccountForm->setOrganizationId('organizationId'); + + $gatewayAccountForm->setMerchantCategoryCode(5734); + + $gatewayAccountForm->setWebsites([ + 'websiteId1', + 'websiteId2', + ]); + + $gatewayAccountForm->setPaymentCardSchemes([ + Rebilly\Entities\PaymentCardScheme::SCHEME_VISA, + Rebilly\Entities\PaymentCardScheme::SCHEME_MASTERCARD, + ]); + + $gatewayAccountForm->setMethod(Rebilly\Entities\PaymentMethod::METHOD_CASH); + + + $gatewayConfig = [ + 'accountId' => 'test', + 'password' => '123', + ]; + + + $gatewayAccountForm->setGatewayConfig($gatewayConfig); + + + try { + $gatewayAccount = $client->gatewayAccounts()->create($gatewayAccountForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the required properties for the new gateway account + + const data = { + gatewayName: 'RebillyProcessor', + acquirerName: 'RebillyProcessor', + merchantCategoryCode: 0, + acceptedCurrencies: ['USD'], + method: 'payment-card', + paymentCardSchemes: [ + 'Visa', 'MasterCard', 'American Express', + 'Discover', 'Diners Club', 'JCB' + ], + // the gatewayConfig varies for each gateway name, + // see the API spec for details + gatewayConfig: {}, + }; + + + // the ID is optional + + const firstKey = await api.gatewayAccounts.create({data}); + + + // or you can provide one + + const secondKey = await api.gatewayAccounts.create({id: + 'my-second-id', data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + + $gatewayAccountForm = new \Rebilly\Sdk\Model\A1Gateway(); + $gatewayAccountForm->setAcquirerName('Bank of Rebilly'); + $gatewayAccountForm->setOrganizationId('organizationId'); + $gatewayAccountForm->setMerchantCategoryCode('5734'); + $gatewayAccountForm->setPaymentCardSchemes([ + 'Visa', + 'MasterCard', + ]); + $gatewayAccountForm->setMethod('cash'); + + try { + $gatewayAccount = $service->gatewayAccounts()->create($gatewayAccountForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/gateway-accounts/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Gateway accounts + summary: Retrieves a gateway account + operationId: GetGatewayAccount + x-sdk-operation-name: get + description: Retrieves a gateway account with a specified ID. + responses: + '200': + description: Gateway account retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $gatewayAccount = + $client->gatewayAccounts()->load('gatewayAccountId'); + - lang: JavaScript + source: >- + const gatewayAccount = await api.gatewayAccounts.get({id: + 'foobar-001'}); + + console.log(gatewayAccount.fields.gatewayName); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + $gatewayAccount = + $service->gatewayAccounts()->get('gatewayAccountId'); + put: + x-products: + - Users + tags: + - Gateway accounts + summary: Upsert a gateway account + operationId: PutGatewayAccount + x-sdk-operation-name: update + description: Creates or updates (upserts) a gateway account with a specified ID. + requestBody: + $ref: '#/components/requestBodies/GatewayAccount' + responses: + '200': + description: Gateway account updated. + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccount' + '201': + description: Gateway account created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: > + $gatewayAccountForm = new Rebilly\Entities\GatewayAccount(); + + + $gatewayAccountForm->setGatewayName('A1Gateway'); + + $gatewayAccountForm->setAcquirerName('Bank of Rebilly'); + + $gatewayAccountForm->setOrganizationId('organizationId'); + + $gatewayAccountForm->setMerchantCategoryCode(5734); + + $gatewayAccountForm->setWebsites([ + 'websiteId1', + 'websiteId2', + ]); + + $gatewayAccountForm->setPaymentCardSchemes([ + Rebilly\Entities\PaymentCardScheme::SCHEME_VISA, + Rebilly\Entities\PaymentCardScheme::SCHEME_MASTERCARD, + ]); + + $gatewayAccountForm->setMethod(Rebilly\Entities\PaymentMethod::METHOD_CASH); + + + $gatewayConfig = [ + 'accountId' => 'test', + 'password' => '123', + ]; + + + $gatewayAccountForm->setGatewayConfig($gatewayConfig); + + + try { + $gatewayAccount = $client->gatewayAccounts()->update('gatewayAccountId', $gatewayAccountForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: >- + // first set the required properties for the new gateway account + + const data = { + gatewayName: 'RebillyProcessor', + acquirerName: 'RebillyProcessor', + merchantCategoryCode: 0, + acceptedCurrencies: ['USD'], + method: 'payment-card', + paymentCardSchemes: [ + 'Visa', 'MasterCard', 'American Express', + 'Discover', 'Diners Club', 'JCB' + ], + // the gatewayConfig varies for each gateway name, + // see the API spec for details + gatewayConfig: {}, + }; + + + // the ID is optional + + const firstKey = await api.gatewayAccounts.create({data}); + + + // or you can provide one + + const secondKey = await api.gatewayAccounts.create({id: + 'my-second-id', data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $gatewayAccountForm = new \Rebilly\Sdk\Model\A1Gateway(); + + $gatewayAccountForm->setAcquirerName('Bank of Rebilly'); + $gatewayAccountForm->setOrganizationId('organizationId'); + $gatewayAccountForm->setMerchantCategoryCode('5734'); + $gatewayAccountForm->setPaymentCardSchemes([ + 'Visa', + 'MasterCard', + ]); + $gatewayAccountForm->setMethod('cash'); + + try { + $gatewayAccount = $service->gatewayAccounts()->update('gatewayAccountId', $gatewayAccountForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + patch: + x-products: + - Users + tags: + - Gateway accounts + summary: Update a gateway account + operationId: PatchGatewayAccount + x-sdk-operation-name: update + description: Updates a gateway account with a specified ID. + requestBody: + $ref: '#/components/requestBodies/GatewayAccount' + responses: + '200': + description: Gateway account updated. + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // build data with only the fields you wish to update + + const data = { + paymentCardSchemes: [ + 'Visa', 'MasterCard', 'American Express' + ] + }; + + + const secondKey = await api.gatewayAccounts.update({id: + 'my-second-id', data}); + delete: + x-products: + - Users + tags: + - Gateway accounts + summary: Delete a gateway account + operationId: DeleteGatewayAccount + x-sdk-operation-name: delete + description: Deletes a gateway account with a specified ID. + responses: + '204': + description: Gateway account deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->gatewayAccounts()->delete('gatewayAccountId'); + - lang: JavaScript + source: >- + const request = await api.gatewayAccounts.delete({id: + 'my-second-key'}); + + + // the request does not return any fields but + + // you can confirm the success using the status code + + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $service->gatewayAccounts()->delete('gatewayAccountId'); + '/gateway-accounts/{id}/close': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Users + tags: + - Gateway accounts + summary: Close a gateway account + operationId: PostGatewayAccountClosure + x-sdk-operation-name: close + description: >- + Closes a gateway account with a specified ID. + + This process is also known as archiving. + + + When a gateway account is closed, + + the gateway cannot process transactions and the `status` attribute is + set to `closed`. + + + >**Important:** Closing a gateway account cannot be undone. Do so with + caution. + responses: + '201': + description: Gateway account closed. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + const gatewayAccount = await api.gatewayAccounts.close({id: + 'foobar-001'}); + + console.log(gatewayAccount.fields.status); + '/gateway-accounts/{id}/disable': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Users + tags: + - Gateway accounts + summary: Disable a gateway account + operationId: PostGatewayAccountDisablement + x-sdk-operation-name: disable + description: >- + Disables a gateway account with a specified ID. + + + When a gateway account is disabled, + + the gateway cannot process transactions and the `status` attribute is + set to `inactive`. + responses: + '201': + description: Gateway account disabled. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + description: Gateway account is pending activation and cannot be disabled. + x-codeSamples: + - lang: JavaScript + source: >- + const gatewayAccount = await api.gatewayAccounts.disable({id: + 'foobar-001'}); + + console.log(gatewayAccount.fields.status); + '/gateway-accounts/{id}/downtime-schedules': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Gateway accounts + summary: List gateway account downtime schedules + operationId: GetGatewayAccountDowntimeScheduleCollection + x-sdk-operation-name: getAllDowntimeSchedules + description: >- + Retrieves a list of downtime schedules for a gateway account with a + specified ID. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of gateway account downtime schedules retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/GatewayAccountDowntimeSchedule' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional except for the `id` + + const firstCollection = await + api.gatewayAccounts.getAllDowntimeSchedules({id: 'my-gateway'}); + + + // alternatively you can specify one or more of them + + const params = {id: 'my-gateway', limit: 20, offset: 100}; + + const secondCollection = await + api.gatewayAccounts.getAllDowntimeSchedules(params); + + + // access the collection items, each item is a Member + + secondCollection.items + .forEach(gatewayAccount => console.log(gatewayAccount.fields.reason)); + post: + x-products: + - Users + tags: + - Gateway accounts + summary: Create a gateway account downtime schedule + operationId: PostGatewayAccountDowntimeSchedule + x-sdk-operation-name: createDowntimeSchedule + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Creates a downtime schedule for a gateway account with a specified ID. + responses: + '201': + description: Gateway account downtime schedule created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountDowntimeSchedule' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + $ref: '#/components/requestBodies/GatewayAccountDowntimeSchedule' + x-codeSamples: + - lang: JavaScript + source: >- + // first set the required properties for + + // the new gateway account downtime schedule + + const data = { + startTime: '2018-08-02T15:13:23Z', + endTime: '2018-08-02T15:13:23Z' + }; + + + // the gateway ID is required + + const secondKey = await + api.gatewayAccounts.createDowntimeSchedule({id: 'my-second-id', + data}); + '/gateway-accounts/{id}/downtime-schedules/{downtimeId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: downtimeId + in: path + description: ID of the gateway account downtime. + required: true + schema: + type: string + get: + x-products: + - Users + tags: + - Gateway accounts + summary: Retrieve a gateway account downtime schedule + operationId: GetGatewayAccountDowntimeSchedule + x-sdk-operation-name: getDowntimeSchedule + description: Retrieves a gateway account downtime schedule with a specified ID. + responses: + '200': + description: Gateway downtime schedule retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountDowntimeSchedule' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + const downtimeSchedule = await + api.gatewayAccounts.getDowntimeSchedule({id: 'foobar-001', + downtimeScheduleId: 'foobar-202'}); + + console.log(downtimeSchedule.fields.reason); + put: + x-products: + - Users + tags: + - Gateway accounts + summary: Update a gateway account downtime schedule + operationId: PutGatewayAccountDowntimeSchedule + x-sdk-operation-name: updateDowntimeSchedule + description: Updates a gateway account downtime schedule with a specified ID. + responses: + '200': + description: Gateway account downtime schedule updated. + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountDowntimeSchedule' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + $ref: '#/components/requestBodies/GatewayAccountDowntimeSchedule' + x-codeSamples: + - lang: JavaScript + source: |- + // first set the required properties for + // the update gateway account downtime schedule + const data = { + startTime: '2018-08-02T15:13:23Z', + endTime: '2018-08-02T15:13:23Z' + }; + + // the gateway ID is required + const secondKey = await api.gatewayAccounts + .updateDowntimeSchedule({id: 'my-second-id', downtimeScheduleId: 'schedule-id', data}); + delete: + x-products: + - Users + tags: + - Gateway accounts + summary: Delete a gateway account downtime schedule + operationId: DeleteGatewayAccountDowntimeSchedule + x-sdk-operation-name: deleteDowntimeSchedule + description: Deletes a gateway account downtime schedule with a specified ID. + responses: + '204': + description: Gateway account downtime schedule deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const request = await api.gatewayAccounts + .deleteDowntimeSchedule({id: 'my-second-key', downtimeScheduleId: 'schedule-id'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + '/gateway-accounts/{id}/enable': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Users + tags: + - Gateway accounts + summary: Enable a gateway account + operationId: PostGatewayAccountEnablement + x-sdk-operation-name: enable + description: >- + Enables a gateway account with a specified ID. + + + When a gateway account is enabled, + + the gateway can process transactions and the `status` attribute is set + to `active`. + responses: + '201': + description: Gateway account enabled. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + description: Gateway account is pending activation and cannot be enabled. + x-codeSamples: + - lang: JavaScript + source: >- + const gatewayAccount = await api.gatewayAccounts.enable({id: + 'foobar-001'}); + + console.log(gatewayAccount.fields.status); + '/gateway-accounts/{id}/limits': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Gateway accounts + summary: Retrieve gateway account limits + operationId: GetGatewayAccountLimitCollection + x-sdk-operation-name: getAllVolumeLimits + description: Retrieves a list of limits for a gateway account with a specified ID. + responses: + '200': + description: List of gateway account limits retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/GatewayAccountLimit' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/gateway-accounts/{id}/limits/{limitId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: limitId + in: path + description: ID of the gateway account limit. + required: true + schema: + type: string + enum: + - daily-money + - monthly-money + - daily-count + - monthly-count + get: + x-products: + - Users + tags: + - Gateway accounts + summary: Retrieve a gateway account limit + operationId: GetGatewayAccountLimit + x-sdk-operation-name: getVolumeLimit + description: Retrieves a gateway account limit with a specified ID. + responses: + '200': + description: Gateway account limit retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountLimit' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Users + tags: + - Gateway accounts + summary: Update a gateway account limit + operationId: PutGatewayAccountLimit + x-sdk-operation-name: updateVolumeLimit + description: Updates a gateway account limit with a specified ID. + responses: + '200': + description: Gateway account limit updated. + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountLimit' + '201': + description: Gateway account limit created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountLimit' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountLimit' + delete: + x-products: + - Users + tags: + - Gateway accounts + summary: Delete a gateway account limit + operationId: DeleteGatewayAccountLimit + x-sdk-operation-name: deleteVolumeLimit + description: Deletes a gateway account limit with a specified ID. + responses: + '204': + description: Gateway account limit deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/gateway-accounts/{id}/timeline': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Gateway accounts timeline + summary: Retrieve gateway account timeline messages + operationId: GetGatewayAccountTimelineCollection + x-sdk-operation-name: getAllTimelineMessages + description: >- + Retrieves a list of timeline messages associated with a specified + gateway account ID. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of gateway account timeline messages retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/GatewayAccountTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional except for the `id` + + const firstCollection = await api.gatewayAccounts + .getAllTimelineMessages({id: 'my-gateway'}); + + // alternatively you can specify one or more of them + + const params = {id: 'my-gateway', limit: 20, offset: 100}; + + const secondCollection = await + api.gatewayAccounts.getAllTimelineMessages(params); + + + // access the collection items, each item is a Member + + secondCollection.items + .forEach(message => console.log(message.fields.eventType)); + post: + x-products: + - Users + tags: + - Gateway accounts timeline + summary: Create a gateway account timeline comment + operationId: PostGatewayAccountTimeline + x-sdk-operation-name: createTimelineComment + description: Creates a timeline comment on gateway account with a specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountTimeline' + description: Gateway account timeline resource. + required: true + responses: + '201': + description: Gateway account timeline comment created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // Create a comment + + const firstComment = await api + .gatewayAccounts.createTimelineComment({id: 'my-gateway-id', data: {message: 'Your comment here'}}); + + // Using params object, mentions and references + + const message = `Example of mentions @user@mydomain.com and + references #customers-customer-id`; + + const params = { + id: 'my-gateway-id', + data: { + message, + }, + }; + + const secondComment = await + api.gatewayAccounts.createTimelineComment(params); + '/gateway-accounts/{id}/timeline/{messageId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: messageId + in: path + description: ID of the gateway account timeline message. + required: true + schema: + type: string + get: + x-products: + - Users + tags: + - Gateway accounts timeline + summary: Retrieve a gateway timeline message + operationId: GetGatewayAccountTimeline + x-sdk-operation-name: getTimelineMessage + description: Retrieves a gateway timeline message with a specified ID. + responses: + '200': + description: Gateway timeline message retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const message = await api.gatewayAccounts + .getTimelineMessage({id: 'foobar-001', messageId: 'message-202'}); + console.log(message.fields.eventType); + delete: + x-products: + - Users + tags: + - Gateway accounts timeline + summary: Delete a gateway timeline message + operationId: DeleteGatewayAccountTimeline + x-sdk-operation-name: deleteTimelineMessage + description: Deletes a gateway timeline message with a specified ID. + responses: + '204': + description: Gateway account timeline message deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: JavaScript + source: |- + const request = await api.gatewayAccounts + .deleteTimelineMessage({id: 'foobar-001', messageId: 'message-202'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + '/gateway-accounts/{id}/check-credentials': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Users + tags: + - Gateway accounts + summary: Verify gateway account credentials + operationId: PostGatewayAccountCheckCredentials + x-sdk-operation-name: checkCredentials + description: >- + Verifies that supplied credentials are valid for a specified gateway + account ID. + responses: + '204': + description: Supplied credentials are valid. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '422': + description: Supplied credentials are not valid. + '/gateway-accounts/{id}/financial-settings': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-badge: Experimental + x-products: + - Users + tags: + - Gateway accounts + summary: Retrieve financial settings of a gateway account + operationId: GetGatewayAccountFinancialSettings + x-sdk-operation-name: getFinancialSettings + description: >- + Retrieves the financial settings of a gateway account with a specified + ID. + + + > **Important:** This operation is experimental and may not be backward + compatible. + responses: + '200': + description: Financial settings retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountFinancialSettings' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-badge: Experimental + x-products: + - Users + tags: + - Gateway accounts + summary: Set financial settings + operationId: PutGatewayAccountFinancialSettings + x-sdk-operation-name: setFinancialSettings + description: >- + Sets financial settings for a gateway account with a specified ID. + + + > **Important:** This operation is experimental and may not be backward + compatible. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountFinancialSettings' + responses: + '200': + description: Gateway account financial settings updated. + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountFinancialSettings' + '201': + description: Gateway account financial settings created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayAccountFinancialSettings' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + /digital-wallets/onboarding/apple-pay: + post: + x-products: + - Users + tags: + - Gateway accounts + summary: Register a domain for Apple Pay + operationId: PostDigitalWalletOnboardingApplePay + x-sdk-operation-name: create + description: |- + Registers a domain for Apple Pay. + + To use this operation: + 1. Download the [Rebilly Apple Pay merchant domain verification file](https://www.rebilly.com/.well-known/apple-developer-merchantid-domain-association), and host it on your domain at `https://{DOMAIN}/.well-known/apple-developer-merchantid-domain-association`. + 1. Use this endpoint to register the domain with Apple Pay, + or to check if it is already registered. + When the domain is registered, you can use Apple Pay with FramePay. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DigitalWalletOnboardingApplePay' + description: Apple pay domain registration request. + required: true + security: + - SecretApiKey: [] + - JWT: [] + responses: + '200': + description: Action executed without error. + content: + application/json: + schema: + $ref: '#/components/schemas/DigitalWalletOnboardingApplePay' + '201': + description: Domain registered with Apple Pay. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/DigitalWalletOnboardingApplePay' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /grid-segments: + get: + x-products: + - Users + tags: + - Segments + summary: Retrieve a user's segments + operationId: GetGridSegmentCollection + x-sdk-operation-name: getAll + description: Retrieves a user's UI segments. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: Segments retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/GridSegment' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Users + tags: + - Segments + summary: Create a new UI segment + operationId: PostGridSegment + x-sdk-operation-name: create + description: >- + Creates a new UI segment for yourself or to share with others within + your organization. + responses: + '201': + description: Segment created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GridSegment' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + $ref: '#/components/requestBodies/GridSegment' + '/grid-segments/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Segments + summary: Retrieve a segment + operationId: GetGridSegment + x-sdk-operation-name: get + description: Retrieves a UI segment with a specified ID. + responses: + '200': + description: Segment retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/GridSegment' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Users + tags: + - Segments + summary: Update a segment + operationId: PutGridSegment + x-sdk-operation-name: update + description: Updates a segment with a specified ID. + responses: + '200': + description: Segment updated. + content: + application/json: + schema: + $ref: '#/components/schemas/GridSegment' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GridSegment' + delete: + x-products: + - Users + tags: + - Segments + summary: Delete a segment + operationId: DeleteGridSegment + x-sdk-operation-name: delete + description: >- + Deletes a segment with a specified ID. + + This operation also removes all shares of the deleted segment. + + + > **Important:** This operation can only be performed by the segment + owner. + responses: + '204': + description: Segment deleted. + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /integrations: + get: + x-products: + - Users + tags: + - Integrations + summary: Retrieve integrations + operationId: GetIntegrationCollection + x-sdk-operation-name: getAll + description: Retrieves a list of integrations. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: List of integrations retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Integration' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/integrations/{label}': + parameters: + - $ref: '#/components/parameters/integrationLabel' + get: + x-products: + - Users + tags: + - Integrations + summary: Retrieve an integration for specific label + operationId: GetIntegration + x-sdk-operation-name: get + description: Retrieves an integration with a specified label. + responses: + '200': + description: Integration retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Integration' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /lists: + get: + x-products: + - Users + tags: + - Lists + summary: Retrieve lists + operationId: GetListCollection + x-sdk-operation-name: getAll + description: |- + Retrieves a collection of lists. + This operation returns the most recent version of each list. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFields' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: Collection of lists retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ValueList' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.lists.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.lists.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(list => + console.log(list.fields.name)); + post: + x-products: + - Users + tags: + - Lists + summary: Create a list + operationId: PostList + x-sdk-operation-name: create + description: Creates a new list. + responses: + '201': + description: List created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ValueList' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ValueList' + x-codeSamples: + - lang: JavaScript + source: >- + // first set the properties for the new list + + const data = { + name: 'My value list', + values: [ + 'foobar-0001', 'foobar-0002', 'foobar-0003', + 'foobar-0004', 'foobar-0005', 'foobar-0006' + ] + }; + + + // the ID is optional + + const firstList = await api.lists.create({data}); + + + // or you can provide one + + const secondList = await api.lists.create({id: 'my-second-key', + data}); + '/lists/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Lists + summary: Retrieve latest list version + operationId: GetList + x-sdk-operation-name: getLatestVersion + description: Retrieves the latest version of a list with a specified ID. + responses: + '200': + description: List retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ValueList' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + // get the latest version + const lastest = await api.lists.get({id: 'foobar-001'}); + + // get an older version + const older = await api.lists.get({id: 'foobar-001', version: 12}); + put: + x-products: + - Users + tags: + - Lists + summary: Upsert a list + operationId: PutList + x-sdk-operation-name: update + description: Creates or updates (upserts) a list with a specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ValueList' + description: List resource. + required: true + responses: + '200': + description: List updated. + content: + application/json: + schema: + $ref: '#/components/schemas/ValueList' + '201': + description: List created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ValueList' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // creating a list + + const data = { + name: 'My value list', + values: [ + 'foobar-0001', 'foobar-0002', 'foobar-0003', + 'foobar-0004', 'foobar-0005', 'foobar-0006' + ] + }; + + + // the ID is optional + + const firstList = await api.lists.create({data}); + + + // or you can provide one + + const secondList = await api.lists.create({id: 'my-second-key', + data}); + + + + + // updating a list + + const data = { + name: 'My better list', + values: [ + 'foobar-0004', 'foobar-0005', 'foobar-0006' + ] + }; + + + const list = await api.lists.update({id: 'my-second-key', data}); + delete: + x-products: + - Users + tags: + - Lists + summary: Delete a list + operationId: DeleteList + x-sdk-operation-name: delete + description: Deletes a list with a specified ID. + responses: + '204': + description: List deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + description: Specified list is in use in a rule and cannot be deleted. + x-codeSamples: + - lang: JavaScript + source: |- + const request = await api.lists.delete({id: 'my-second-key'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + '/lists/{id}/{version}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: version + in: path + required: true + description: Version of the list. + schema: + type: integer + minimum: 1 + get: + x-products: + - Users + tags: + - Lists + summary: Retrieve specific list version + operationId: GetListVersion + x-sdk-operation-name: getByVersion + description: Retrieves a specified version of a list with a specified ID. + responses: + '200': + description: List version retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ValueList' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /memberships: + get: + x-products: + - Users + tags: + - Memberships + summary: Retrieve memberships + operationId: GetMembershipCollection + x-sdk-operation-name: getAll + description: Retrieves a list of organization memberships. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of organization memberships retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Membership' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/memberships/{organizationId}/{userId}': + parameters: + - name: organizationId + in: path + required: true + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + schema: + $ref: '#/components/schemas/ResourceId' + - name: userId + in: path + required: true + description: ID of the user. + schema: + $ref: '#/components/schemas/ResourceId' + get: + x-products: + - Users + tags: + - Memberships + summary: Retrieve a membership + operationId: GetMembership + x-sdk-operation-name: get + description: Retrieves a membership with a specified organization ID and user ID. + responses: + '200': + description: Membership retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Membership' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Users + tags: + - Memberships + summary: Upsert membership + operationId: PutMembership + x-sdk-operation-name: update + description: >- + Creates or updates (upserts) a membership with a specified organization + ID and user ID. + + Only the organization owner can grant organization membership to new + users. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Membership' + required: true + responses: + '200': + description: Membership updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Membership' + '201': + description: Membership created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Membership' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + delete: + x-products: + - Users + tags: + - Memberships + summary: Delete a membership + operationId: DeleteMembership + x-sdk-operation-name: delete + description: Deletes a membership with a specified organization ID and user ID. + responses: + '204': + description: Membership deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + /organizations: + get: + x-products: + - Users + tags: + - Organizations + summary: Retrieve organizations + operationId: GetOrganizationCollection + x-sdk-operation-name: getAll + description: Retrieves a list of organizations. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of organizations retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Organization' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Users + tags: + - Organizations + summary: Create an organization + operationId: PostOrganization + x-sdk-operation-name: create + description: Creates an organization. + requestBody: + $ref: '#/components/requestBodies/PostOrganization' + responses: + '201': + description: Organization created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Organization' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $organizationForm = new Rebilly\Entities\Organization(); + $organizationForm->setName('Test Organization'); + $organizationForm->setCountry('US'); + + try { + $organization = $client->organizations()->create($organizationForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + $organizationForm = new + \Rebilly\Sdk\Model\PostOrganizationRequest(); + + $organizationForm->setName('Test Organization'); + + $organizationForm->setCountry('US'); + + + try { + $organization = $service->organizations()->create($organizationForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/organizations/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Organizations + summary: Retrieve an organization + operationId: GetOrganization + x-sdk-operation-name: get + description: Retrieves an organization with a specified ID. + responses: + '200': + description: Organization retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Organization' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $organization = $client->organizations()->load('organizationId'); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $organization = $service->organizations()->get('organizationId'); + patch: + x-products: + - Users + tags: + - Organizations + summary: Update an organization + operationId: PatchOrganization + x-sdk-operation-name: update + description: Updates an organization with a specified organization ID. + requestBody: + $ref: '#/components/requestBodies/PatchOrganization' + responses: + '200': + description: Organization updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Organization' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $organizationForm = new Rebilly\Entities\Organization(); + $organizationForm->setName('Test Organization'); + $organizationForm->setCountry('US'); + + try { + $organization = $client->organizations()->update('organizationId', $organizationForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + + $organizationForm = new + \Rebilly\Sdk\Model\PatchOrganizationRequest(); + + $organizationForm->setName('Test Organization'); + + $organizationForm->setCountry('US'); + + + try { + $organization = $service->organizations()->update('organizationId', $organizationForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + /payment-cards-bank-names: + get: + x-products: + - Users + tags: + - Payment instruments + summary: Retrieve payment card issuing bank names + operationId: GetPaymentCardBankNameCollection + x-sdk-operation-name: getAll + description: Retrieves a list of payment card issuing bank names. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of payment card issuing bank names retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + title: GetPaymentCardsBankNamesResponse + items: + type: object + properties: + name: + description: Name of the bank. + type: string + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + /payment-methods: + servers: + - url: 'https://api-sandbox.rebilly.com' + description: Sandbox server + - url: 'https://api.rebilly.com' + description: Live server + get: + x-products: + - Users + tags: + - Metadata + summary: Retrieve payment method metadata + operationId: GetPaymentMethodCollection + x-sdk-operation-name: getAll + description: Retrieves payment method metadata. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + security: [] + responses: + '200': + description: Payment methods metadata received. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PaymentMethodMetadata' + '/payment-methods/{apiName}': + servers: + - url: 'https://api-sandbox.rebilly.com' + description: Sandbox server + - url: 'https://api.rebilly.com' + description: Live server + parameters: + - name: apiName + in: path + description: Name of the payment method API. + required: true + schema: + description: Name of the payment method returned in the API response. + type: string + pattern: '^[\w\. -]+$' + get: + x-products: + - Users + tags: + - Metadata + summary: Retrieve metadata of a payment method + operationId: GetPaymentMethod + x-sdk-operation-name: get + description: Retrieves the metadata of a payment method with a specified `apiName`. + security: [] + responses: + '200': + description: Payment method metadata received. + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentMethodMetadata' + /payment-gateways-metadata: + get: + x-products: + - Users + tags: + - Metadata + summary: Retrieve payment gateway metadata + operationId: GetPaymentGatewayMetadataCollection + x-sdk-operation-name: getAll + description: Retrieves payment gateway metadata. + security: [] + responses: + '200': + description: Payment gateway metadata received. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PaymentGatewayMetadata' + '/payment-gateways-metadata/{apiName}': + parameters: + - name: apiName + in: path + description: Payment gateway metadata API name. + required: true + schema: + description: Name of the payment gateway returned in the API response. + type: string + pattern: '^[\w\. -]+$' + get: + x-products: + - Users + tags: + - Metadata + summary: Retrieve payment gateway metadata + operationId: GetPaymentGatewayMetadata + x-sdk-operation-name: get + description: Retrieves metadata for a gateway with a specified ID. + security: [] + responses: + '200': + description: Payment gateway metadata received. + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentGatewayMetadata' + /payout-requests: + get: + x-products: + - Core + tags: + - Transactions + summary: Retrieve a list of payout requests + operationId: GetPayoutRequestCollection + x-sdk-operation-name: getAll + description: Retrieves a list of payout requests. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of payout requests retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PayoutRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Transactions + summary: Create a payout request + operationId: PostPayoutRequest + x-sdk-operation-name: create + description: >- + Creates a payout request. + + In the response, the `selectPaymentInstrumentUrl` field is used to + redirect the customer to select a preferred payment instrument. + + After a payment instrument is selected, the customer is redirected to + the `selectedPaymentInstrumentRedirectUrl` value. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutRequest' + description: Payout request resource. + required: true + responses: + '201': + description: Payout request created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/payout-requests/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Transactions + summary: Retrieve a payout request + operationId: GetPayoutRequest + x-sdk-operation-name: get + description: Retrieves a payout request with a specified ID. + responses: + '200': + description: Payout request retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + tags: + - Transactions + summary: Upsert a payout request + operationId: PutPayoutRequest + x-sdk-operation-name: update + description: Creates or updates (upserts) a payout request with a specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutRequest' + responses: + '200': + description: Payout request updated. + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutRequest' + '201': + description: Payout request created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + '/payout-requests/{id}/payment-instruments': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Transactions + summary: Retrieve associated payment instruments + operationId: GetPayoutRequestPaymentInstruments + x-sdk-operation-name: getPaymentInstruments + description: >- + Retrieves the payment gateway and a list of payment instruments that are + associated with a payout request with a specified ID. + responses: + '200': + description: Payout request associated payment instruments retrieved. + content: + application/json: + schema: + type: array + description: >- + List of available payment instruments per an associated + gateway to offer for the payout. + items: + type: object + properties: + paymentInstrumentId: + type: string + description: ID of the payment instrument to offer for the payout. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + gatewayName: + description: >- + Name of the payment gateway and payment instrument to + use for the payout. + + This field is empty if the requested payment instrument + has not been used yet. + x-label: Gateway account + x-basic: true + type: + - string + - 'null' + allOf: + - $ref: '#/components/schemas/GatewayName' + exposureAmount: + description: >- + Total amount of sales minus credit for the specified + gateway and instrument. + type: number + format: double + previousAllocatedAmount: + description: >- + Total amount of allocated credit for the payout request + for the specified gateway and instrument. + type: number + format: double + lastPaymentTime: + description: >- + Date and time when the last payment is created for the + gateway and the payment instrument. + type: string + format: date-time + lastPayoutTime: + description: >- + Date and time when the last payout is created for the + gateway and the payment instrument. + type: string + format: date-time + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /permissions-emulation: + post: + x-products: + - Users + tags: + - Profile + summary: Start permissions emulation + operationId: PostPermissionsEmulation + x-sdk-operation-name: startPermissionsEmulation + description: >- + Starts permissions emulation. + + + Emulation during emulation is not supported. If request sent during an + ongoing emulation then 403 is sent in response. + + + Escalation is not supported. + requestBody: + content: + application/json: + schema: + type: object + title: PostPermissionsEmulationRequest + required: + - permissions + properties: + permissions: + description: List of permissions to be emulated. + $ref: '#/components/schemas/AclPermissions' + required: true + responses: + '201': + description: Session created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Session' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + delete: + x-products: + - Users + tags: + - Profile + summary: Stop permissions emulation + operationId: DeletePermissionsEmulation + x-sdk-operation-name: stopPermissionsEmulation + description: |- + Stops permissions emulation. + + Session permissions are restored to the state before emulation began. + If emulations have not started then `403` is sent in response. + responses: + '201': + description: Session restored. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Session' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + /previews/orders: + post: + x-products: + - Core + tags: + - Orders + summary: Preview an order + operationId: PostPreviewOrder + x-sdk-operation-name: order + description: |- + Previews an order. + Use this operation to preview a draft order before making an order. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/OrderPreview' + responses: + '200': + description: Order preview retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/OrderPreview' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /previews/rule-actions/send-email: + post: + x-products: + - Users + tags: + - Rules + summary: Send a test email + operationId: PostPreviewRuleActionEmailSending + x-sdk-operation-name: sendEmailRuleAction + description: Sends a test email. + requestBody: + content: + application/json: + schema: + description: Send a test email. + $ref: '#/components/schemas/RulesEmailNotification' + description: Test email resource. + required: true + responses: + '200': + description: Test email sent. + content: + application/json: + schema: + description: Send a test email. + $ref: '#/components/schemas/RulesEmailNotification' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: |- + // first build the email data including + // the credential hash + const data = { + bodyText: 'hello world', + bodyHTML: `hello world`, + sender: 'john.doe+test@grr.la', + recipients: ['john.doe+test@grr.la'], + subject: 'testing email preview', + // created prior to the test + credentialHash: 'dcf6e32f2daee457a1db8ce5fdfbe200' + }; + + const email = await api.previews.sendEmailRuleAction({data}); + /previews/webhooks: + post: + x-products: + - Users + tags: + - Webhooks + summary: Trigger a test webhook + operationId: PostPreviewWebhook + x-sdk-operation-name: webhook + description: Triggers a test webhook. + requestBody: + $ref: '#/components/requestBodies/GlobalWebhook' + responses: + '204': + description: Test webhook triggered. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + description: Invalid data sent. + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidError' + x-codeSamples: + - lang: JavaScript + source: |- + // first build the webhook data including + // the credential hash + const data = { + eventsFilter: [], + status: 'active', + method: 'POST', + headers: {}, + // hookb.in is a great tool for + // previewing webhooks + url: 'https://hookb.in/Oe90ZRmdeWUGWg1MGKQV', + // created prior to the test + credentialHash: 'dcf6e32f2daee457a1db8ce5fdfbe200' + }; + + const request = await api.previews.webhooks({data}); + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + /profile: + get: + x-products: + - Users + tags: + - Profile + summary: Retrieve user's own profile + operationId: GetProfile + x-sdk-operation-name: get + description: Retrieves the user's own profile as requested by the user. + responses: + '200': + description: Profile retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Profile' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const profile = await api.profile.get(); + console.log(profile.fields.email); + put: + x-products: + - Users + tags: + - Profile + summary: Update a user profile + operationId: PutProfile + x-sdk-operation-name: update + description: Updates user profile. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Profile' + description: Profile resource. + required: true + responses: + '200': + description: Profile updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Profile' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: | + // define values to update + const data = { + preferences: [], + }; + + const profile = await api.profile.update({data}); + /profile/mfa: + get: + x-products: + - Users + tags: + - Profile + summary: Retrieve user MFA status + operationId: GetProfileMfa + x-sdk-operation-name: getMfa + description: Retrieves Multi-Factor Authentication (MFA) status of a user. + responses: + '200': + description: User MFA status retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ProfileMfa' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: > + const mfa = await api.profile.getMfa(); + + console.log(mfa.fields.status, mfa.fields.type, + mfa.fields.lastAuthTime); + post: + x-products: + - Users + tags: + - Profile + summary: Update user MFA + operationId: PostProfileMfa + x-sdk-operation-name: updateMfa + description: >- + Updates the Multi-Factor Authentication (MFA) information of a user. + + + The generated link with an `enrollment` `rel` link must be followed to + verify the existing MFA or enroll new MFA. + responses: + '201': + description: User MFA update URL created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/ProfileMfa' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: | + const mfa = await api.profile.updateMfa(); + console.log(mfa.fields._links); + delete: + x-products: + - Users + tags: + - Profile + summary: Delete user MFA + operationId: DeleteProfileMfa + x-sdk-operation-name: deleteMfa + description: >- + Deletes the Multi-Factor Authentication (MFA) information of a user. + + + To complete this operation, the `lastAuthTime` value must be no more + than 10 minutes before this request. + responses: + '204': + description: User MFA deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: | + await api.profile.deleteMfa(); + /roles: + get: + x-products: + - Users + tags: + - Roles + summary: Retrieve roles + operationId: GetRoleCollection + x-sdk-operation-name: getAll + description: Retrieves a list of user roles. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: List of roles retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Role' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Users + tags: + - Roles + summary: Create a role + operationId: PostRole + x-sdk-operation-name: create + description: Creates a role that is used to assign permissions to users. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Role' + description: Role resource. + required: true + responses: + '201': + description: Role created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Role' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/roles/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Roles + summary: Retrieve a role + operationId: GetRole + x-sdk-operation-name: get + description: Retrieves a user role with a specified ID. + parameters: + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Role retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Role' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Users + tags: + - Roles + summary: Create a role with ID + operationId: PutRole + x-sdk-operation-name: update + description: Creates a user role with a specified ID. + responses: + '200': + description: Role updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Role' + '201': + description: Role created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Role' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Role' + description: Role resource. + required: true + delete: + x-products: + - Users + tags: + - Roles + summary: Delete a role + operationId: DeleteRole + x-sdk-operation-name: delete + description: Deletes a user role with a specified ID. + responses: + '204': + description: Role deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/send-through-attribution/{eventType}': + parameters: + - $ref: '#/components/parameters/systemEventType' + get: + x-products: + - Users + tags: + - Email notifications + summary: Retrieve email notification attributions + operationId: GetSendThroughAttributionCollection + x-sdk-operation-name: getAll + description: >- + Retrieves a list of email notifications, that have been sent through + attribution, for a specified event type. + responses: + '200': + description: List of email notifications retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SendThroughAttribution' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + /status: + servers: + - url: 'https://api-sandbox.rebilly.com' + description: Sandbox server + - url: 'https://api.rebilly.com' + description: Live server + get: + x-products: + - Users + tags: + - Status + summary: Retrieve API current status + operationId: GetStatus + x-sdk-operation-name: get + description: Retrieve API current status. + security: [] + responses: + '200': + description: Status received. + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + x-codeSamples: + - lang: JavaScript + source: |- + const status = await api.status.get({id: 'foobar-001'}); + console.log(status.fields.status); + /tracking/api: + get: + x-products: + - Users + tags: + - Tracking + summary: Retrieve API tracking logs + operationId: GetTrackingApiCollection + x-sdk-operation-name: getAllApiLogs + description: Retrieves API tracking log records. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Tracking API logs retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiTracking' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $apiTrackingLog = $client->apiTracking()->search([ + 'filter' => 'status:200', + ]); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.tracking.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100}; + + const secondCollection = await api.tracking.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(entry => + console.log(entry.fields.customerId)); + + + + + // alternatively, download as a CSV file + + + // all parameters are optional + + const firstFile = await api.tracking.downloadApiLogsCSV(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondFile = await api.tracking.downloadApiLogsCSV(params); + + + // access the file data to view the CSV content + + console.log(secondFile.data); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + + $apiTrackingLogsPaginator = + $service->tracking()->getAllApiLogsPaginator(limit: 5, filter: + 'status:200'); + + foreach ($apiTrackingLogsPaginator as $apiTrackingLogsPage) { + printf("API tracking logs page %d/%d\n", $apiTrackingLogsPaginator->key() + 1, $apiTrackingLogsPaginator->count()); + foreach ($apiTrackingLogsPage as $apiTrackingLog) { + printf("API tracking log #%s: %s\n", $apiTrackingLog->getId(), $apiTrackingLog->getRequest()); + } + } + + + // OR + + + $apiTrackingLogs = $service->tracking()->getAllApiLogs(filter: + 'status:200'); + + foreach ($apiTrackingLogs as $apiTrackingLog) { + printf("API tracking log #%s: %s\n", $apiTrackingLog->getId(), $apiTrackingLog->getRequest()); + } + '/tracking/api/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Tracking + summary: Retrieve an API tracking log + operationId: GetTrackingApi + x-sdk-operation-name: getApiLog + description: Retrieves an API tracking log record with a specified ID. + responses: + '200': + description: Tracking API log retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ApiTracking' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $apiTrackingLog = $client->apiTracking()->load('apiLogId'); + - lang: JavaScript + source: |- + const entry = await api.tracking.getApiLog({id: 'foobar-001'}); + console.log(entry.fields.request); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $apiTrackingLog = $service->tracking()->getApiLog('apiLogId'); + /tracking/taxes: + get: + x-products: + - Users + tags: + - Tracking + summary: Retrieve tax service tracking logs + operationId: GetTrackingTaxCollection + x-sdk-operation-name: getAllTaxTrackingLogs + description: Retrieves a collection of tax service tracking logs. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: Tax logs retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/TaxTracking' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await api.tracking.getAllTaxTrackingLogs(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100}; + + const secondCollection = await + api.tracking.getAllTaxTrackingLogs(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(entry => + console.log(entry.fields.eventType)); + '/tracking/taxes/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Tracking + summary: Retrieve a tax service tracking log + operationId: GetTrackingTax + x-sdk-operation-name: getTaxTrackingLog + description: Retrieves a tax service tracking log with a specified ID. + responses: + '200': + description: Tax log retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/TaxTracking' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: > + const entry = await api.tracking.getTaxTrackingLog({id: + 'foobar-001'}); + + console.log(entry.fields.eventType); + /tracking/lists: + get: + x-products: + - Users + tags: + - Tracking + summary: Retrieve value list changes + operationId: GetTrackingListCollection + x-sdk-operation-name: getAllListsChangesHistory + description: Retrieves the change history of value lists. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: Change history of a value list retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ValueList' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await + api.tracking.getAllListsChangesHistory(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100}; + + const secondCollection = await + api.tracking.getAllListsChangesHistory(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(entry => + console.log(entry.fields.name)); + /tracking/webhooks: + get: + x-products: + - Users + tags: + - Tracking + summary: Retrieve webhook tracking logs + operationId: GetTrackingWebhookCollection + x-sdk-operation-name: getAllWebhookTrackingLogs + description: Retrieves webhook tracking logs. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: Tracking webhook logs retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/WebhookTracking' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await + api.tracking.getAllWebhookTrackingLogs(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100}; + + const secondCollection = await + api.tracking.getAllWebhookTrackingLogs(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(entry => + console.log(entry.fields.eventType)); + '/tracking/webhooks/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Tracking + summary: Retrieve a webhook tracking log + operationId: GetTrackingWebhook + x-sdk-operation-name: getWebhookTrackingLog + description: Retrieves a webhook tracking log record with a specified ID. + responses: + '200': + description: Webhook tracking log record retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/WebhookTracking' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + const entry = await api.tracking.getWebhookTrackingLog({id: + 'foobar-001'}); + + console.log(entry.fields.eventType); + '/tracking/webhooks/{id}/resend': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Users + tags: + - Tracking + summary: Schedule a resend webhook attempt + description: |- + Schedules a resend attempt for a specified webhook tracking ID. + Webhooks are processed asynchronously. + operationId: PostTrackingWebhookResendRequest + x-sdk-operation-name: resendWebhook + responses: + '204': + description: Webhook resend attempt scheduled. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /users: + get: + x-products: + - Users + tags: + - Users + summary: Retrieve a list of users + operationId: GetUserCollection + x-sdk-operation-name: getAll + description: Retrieves a list of users. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of users retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $users = $client->users()->search([ + 'filter' => 'firstName:John', + ]); + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.users.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.users.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(user => + console.log(user.fields.firstName)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + + $usersPaginator = $service->users()->getAllPaginator(limit: 5, + filter: 'firstName:John'); + + foreach ($usersPaginator as $usersPage) { + printf("Users page %d/%d\n", $usersPaginator->key() + 1, $usersPaginator->count()); + foreach ($usersPage as $user) { + printf("User #%s: %s %s | %s\n", $user->getId(), $user->getFirstName(), $user->getLastName(), $user->getEmail()); + } + } + + + // OR + + + $users = $service->users()->getAll(filter: 'firstName:John'); + + foreach ($users as $user) { + printf("User #%s: %s %s | %s\n", $user->getId(), $user->getFirstName(), $user->getLastName(), $user->getEmail()); + } + post: + x-products: + - Users + tags: + - Users + summary: Create a user + operationId: PostUser + x-sdk-operation-name: create + description: Creates a user. + requestBody: + $ref: '#/components/requestBodies/User' + responses: + '201': + description: User created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $userForm = new Rebilly\Entities\User(); + $userForm->setFirstName('John'); + $userForm->setLastName('Doe'); + $userForm->setEmail('johndoe@test.com'); + + try { + $user = $client->users()->create($userForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // first set the properties for the new user + + const data = { + email: 'john.doe+test@grr.la', + firstName: 'John', + lastName: 'Doe', + businessPhone: '151412345676', + mobilePhone: '151412345676', + permissions: [], + country: 'US', + preferences: {} + }; + + + // the ID is optional + + const firstProduct = await api.users.create({data}); + + + // or you can provide one + + const secondProduct = await api.users.create({id: 'my-second-key', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + + $userForm = new \Rebilly\Sdk\Model\User(); + $userForm->setFirstName('John'); + $userForm->setLastName('Doe'); + $userForm->setEmail('johndoe@test.com'); + + try { + $user = $service->users()->create($userForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/users/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Users + summary: Retrieve a user + operationId: GetUser + x-sdk-operation-name: get + description: Retrieves a user with a specified ID. + responses: + '200': + description: User retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $user = $client->users()->load('userId'); + - lang: JavaScript + source: |- + const user = await api.users.get({id: 'foobar-001'}); + console.log(user.fields.firstName); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $user = $service->users()->get('userId'); + put: + x-products: + - Users + tags: + - Users + summary: Upsert a user + operationId: PutUser + x-sdk-operation-name: update + description: Creates or updates (upserts) a user with a specified ID. + requestBody: + $ref: '#/components/requestBodies/User' + responses: + '200': + description: User updated. + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '201': + description: User created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $userForm = new Rebilly\Entities\User(); + $userForm->setFirstName('John'); + $userForm->setLastName('Doe'); + $userForm->setEmail('johndoe@test.com'); + + try { + $user = $client->users()->update('userId', $userForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // creating a new user + + const data = { + email: 'john.doe+test@grr.la', + firstName: 'John', + lastName: 'Doe', + businessPhone: '151412345676', + mobilePhone: '151412345676', + permissions: [], + country: 'US', + preferences: {} + }; + + + // the ID is optional + + const firstProduct = await api.users.create({data}); + + + // or you can provide one + + const secondProduct = await api.users.create({id: 'my-second-key', + data}); + + + + + // updating a user + + const data = { + email: 'john.doe+test@grr.la', + firstName: 'John', + lastName: 'Doe', + country: 'CA', + }; + + + const user = await api.users.update({id: 'my-second-key', data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + + $userForm = new \Rebilly\Sdk\Model\User(); + $userForm->setFirstName('John'); + $userForm->setLastName('Doe'); + $userForm->setEmail('johndoe@test.com'); + + try { + $user = $service->users()->update('userId', $userForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/users/{id}/mfa': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Users + summary: Retrieve user MFA status + operationId: GetUserMfa + x-sdk-operation-name: getMfa + description: >- + Retrieves the Multi-Factor Authentication (MFA) status of a user with a + specified ID. + responses: + '200': + description: User MFA status retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ProfileMfa' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: > + const mfa = await api.users.get({id: 'foobar-001'}); + + console.log(mfa.fields.status, mfa.fields.type, + mfa.fields.lastAuthTime); + /webhooks: + get: + x-products: + - Users + tags: + - Webhooks + summary: Retrieve webhooks + operationId: GetWebhookCollection + x-sdk-operation-name: getAll + description: Retrieves a list of webhooks. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: List of webhooks retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/GlobalWebhook' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.webhooks.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.webhooks.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(webhook => + console.log(webhook.fields.name)); + post: + x-products: + - Users + tags: + - Webhooks + summary: Create a webhook + operationId: PostWebhook + x-sdk-operation-name: create + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Creates a webhook. + requestBody: + $ref: '#/components/requestBodies/GlobalWebhook' + responses: + '201': + description: Webhook created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GlobalWebhook' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // first set the properties for the new webhook + + const data = { + // leave filter empty to enable + // the webhook for all events + eventsFilter: [], + status: 'active', + method: 'POST', + headers: {}, + url: 'https://hookb.in/Oe90ZRmdeWUGWg1MGKQV', + // created prior to the test + credentialHash: 'dcf6e32f2daee457a1db8ce5fdfbe200' + }; + + + // the ID is optional + + const firstWebhook = await api.webhooks.create({data}); + + + // or you can provide one + + const secondWebhook = await api.webhooks.create({id: + 'my-second-key', data}); + '/webhooks/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Webhooks + summary: Retrieve a webhook + operationId: GetWebhook + x-sdk-operation-name: get + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a webhook with a specified ID. + responses: + '200': + description: Webhook retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/GlobalWebhook' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: |- + const webhook = await api.webhooks.get({id: 'foobar-001'}); + console.log(webhook.fields.id); + put: + x-products: + - Users + tags: + - Webhooks + summary: Upsert a webhook + operationId: PutWebhook + x-sdk-operation-name: update + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Creates or updates (upserts) a webhook with a specified ID. + requestBody: + $ref: '#/components/requestBodies/GlobalWebhook' + responses: + '200': + description: Webhook updated. + content: + application/json: + schema: + $ref: '#/components/schemas/GlobalWebhook' + '201': + description: Webhook created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GlobalWebhook' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + description: Invalid data sent. + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidError' + /websites: + get: + x-products: + - Users + tags: + - Websites + summary: Retrieve websites + operationId: GetWebsiteCollection + x-sdk-operation-name: getAll + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a list of websites. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of websites retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Website' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $websites = $client->websites()->search([ + 'filter' => 'name:TestWebsite', + ]); + - lang: JavaScript + source: > + // all parameters are optional + + const firstCollection = await api.websites.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.websites.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(website => + console.log(website.fields.name)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\UsersService($client); + + + $websitesPaginator = $service->websites()->getAllPaginator(limit: 5, + filter: 'name:TestWebsite'); + + foreach ($websitesPaginator as $websitesPage) { + printf("Websites page %d/%d\n", $websitesPaginator->key() + 1, $websitesPaginator->count()); + foreach ($websitesPage as $website) { + printf("Website #%s: %s\n", $website->getId(), $website->getName()); + } + } + + + // OR + + + $websites = $service->websites()->getAll(filter: + 'name:TestWebsite'); + + foreach ($websites as $website) { + printf("Website #%s: %s\n", $website->getId(), $website->getName()); + } + post: + x-products: + - Users + tags: + - Websites + summary: Create a website + operationId: PostWebsite + x-sdk-operation-name: create + description: Creates a website. + requestBody: + $ref: '#/components/requestBodies/Website' + responses: + '201': + description: Website created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Website' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $websiteForm = new Rebilly\Entities\Website(); + $websiteForm->setName('TestWebsite'); + $websiteForm->setUrl('http://testwebsite.com'); + $websiteForm->setServicePhone('+0123456789'); + $websiteForm->setServiceEmail('test@testwebsite.com'); + + try { + $website = $client->websites()->create($websiteForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // first set the properties for the new website + + const data = { + name: 'My website', + url: 'https://www.acme.com', + servicePhone: '15451234567', + serviceEmail: 'support@acme.com', + customFields: {} + }; + + + // the ID is optional + + const firstWebsite = await api.websites.create({data}); + + + // or you can provide one + + const secondWebsite = await api.websites.create({id: + 'my-second-key', data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + + $websiteForm = new \Rebilly\Sdk\Model\Website(); + $websiteForm->setName('TestWebsite'); + $websiteForm->setUrl('http://testwebsite.com'); + $websiteForm->setServicePhone('+0123456789'); + $websiteForm->setServiceEmail('test@testwebsite.com'); + + try { + $website = $service->websites()->create($websiteForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + '/websites/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Websites + summary: Retrieve a website + operationId: GetWebsite + x-sdk-operation-name: get + security: + - SecretApiKey: [] + - JWT: [] + - ApplicationJWT: [] + description: Retrieves a website with a specified ID. + responses: + '200': + description: Website retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Website' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $website = $client->websites()->load('websiteId'); + - lang: JavaScript + source: |- + const website = await api.websites.get({id: 'foobar-001'}); + console.log(website.fields.name); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $website = $service->websites()->get('websiteId'); + put: + x-products: + - Users + tags: + - Websites + summary: Upsert a website + operationId: PutWebsite + x-sdk-operation-name: update + description: Creates or updates (upserts) a website with a specified ID. + requestBody: + $ref: '#/components/requestBodies/Website' + responses: + '200': + description: Website updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Website' + '201': + description: Website created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Website' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $websiteForm = new Rebilly\Entities\Website(); + $websiteForm->setName('TestWebsite'); + $websiteForm->setUrl('http://testwebsite.com'); + $websiteForm->setServicePhone('+0123456789'); + $websiteForm->setServiceEmail('test@testwebsite.com'); + + try { + $website = $client->websites()->update('websiteId', $websiteForm); + } catch (Rebilly\Http\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + - lang: JavaScript + source: > + // creating a new website + + const data = { + name: 'My website', + url: 'https://www.acme.com', + servicePhone: '15451234567', + serviceEmail: 'support@acme.com', + customFields: {} + }; + + + // the ID is optional + + const firstWebsite = await api.websites.create({data}); + + + // or you can provide one + + const secondWebsite = await api.websites.create({id: + 'my-second-key', data}); + + + + + // updating a website + + const data = { + name: 'My website', + url: 'https://www.acme.com', + servicePhone: '15451234567', + serviceEmail: 'support@acme.com', + customFields: {} + }; + + + const website = await api.websites.update({id: 'my-second-key', + data}); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + + $websiteForm = new \Rebilly\Sdk\Model\Website(); + $websiteForm->setName('TestWebsite'); + $websiteForm->setUrl('http://testwebsite.com'); + $websiteForm->setServicePhone('+0123456789'); + $websiteForm->setServiceEmail('test@testwebsite.com'); + + try { + $website = $service->websites()->update('websiteId', $websiteForm); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Users + tags: + - Websites + summary: Delete a website + operationId: DeleteWebsite + x-sdk-operation-name: delete + description: Deletes a website with a specified ID. + responses: + '204': + description: Website deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + x-codeSamples: + - lang: PHP + label: PHP-SDK-2.0 + source: | + $client->websites()->delete('websiteId'); + - lang: JavaScript + source: |- + const request = await api.websites.delete({id: 'my-second-key'}); + + // the request does not return any fields but + // you can confirm the success using the status code + console.log(request.response.status); // 204 + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\UsersService($client); + $service->websites()->delete('websiteId'); + '/experimental/customers/{customerId}/summary-metrics': + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + parameters: + - name: customerId + in: path + required: true + description: ID of the customer. + schema: + type: string + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Customers + summary: Retrieve a customer's lifetime summary metrics + operationId: GetCustomerSummaryMetricReport + x-sdk-operation-name: getCustomerLifetimeSummaryMetrics + description: Retrieves lifetime summary metrics for a customer with a specified ID. + responses: + '200': + description: Metrics retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerInformation' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + const metrics = await + api.customers.getCustomerLifetimeSummaryMetrics({id: + 'foobar-0001'}); + + console.log(metrics.fields.revenueAmount); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\ReportsService($client); + + + $metrics = + $service->customers()->getCustomerLifetimeSummaryMetrics('customerId'); + /experimental/data-exports: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + post: + x-products: + - Reports + tags: + - Data exports + summary: Request the data export of a resource + operationId: PostDataExport + x-sdk-operation-name: queue + description: Requests the export of a specific data resource. + parameters: + - $ref: '#/components/parameters/collectionExpand' + requestBody: + $ref: '#/components/requestBodies/DataExport' + responses: + '201': + description: Data export request received. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/DataExport' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: >- + // first prepare the details of the export + + const data = { + name: 'my-first-export', + // only CSV is currently supported + format: 'csv', + // define filters, criteria, etc. + // to reduce the amount of values exported + arguments: {}, + dateRange: { + range: 'all' + }, + // list email addresses that will receive + // a notification once the download is ready + emailNotification: [''] + }; + + + const queuedExport = await api.exports.queue({resource: + 'transaction', data}); + + console.log(queuedExport.fields.status); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $dataExport = new \Rebilly\Sdk\Model\Customers([ + 'name' => 'Daily customer export', + 'format' => 'csv', + 'fields' => [ + 'firstName', + 'lastName', + 'email', + 'lifetimeRevenue/amount', + 'lastPaymentTime', + 'createdTime', + ], + 'recurring' => [ + 'instruction' => 'RRULE:FREQ=DAILY', + ], + 'dateRange' => [ + 'start' => 'yesterday', + 'end' => 'today', + ], + 'emailNotification' => [ + 'user@example.com', + ], + ]); + + $service->dataExports()->queue($dataExport); + get: + x-products: + - Reports + tags: + - Data exports + summary: Retrieve data export requests + operationId: GetDataExportCollection + x-sdk-operation-name: getAll + description: Retrieves a list of data export requests. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionExpand' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionCriteria' + responses: + '200': + description: List of data export requests retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DataExport' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + // all parameters are optional + + const firstCollection = await api.exports.getAll(); + + + // alternatively you can specify one or more of them + + const params = {limit: 20, offset: 100, sort: '-createdTime'}; + + const secondCollection = await api.exports.getAll(params); + + + // access the collection items, each item is a Member + + secondCollection.items.forEach(file => + console.log(exports.fields.status)); + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\ReportsService($client); + + + $dataExportsPaginator = + $service->dataExports()->getAllPaginator(limit: 5); + + foreach ($dataExportsPaginator as $dataExportPage) { + printf("dataExports page %d/%d\n", $dataExportsPaginator->key() + 1, $dataExportsPaginator->count()); + foreach ($dataExportPage as $dataExport) { + printf("DataExport #%s: %s\n", $dataExport->getId(), $dataExport->getName()); + } + } + + + // OR + + + $dataExports = $service->dataExports()->getAll(limit: 100); + + foreach ($dataExports as $dataExport) { + printf("DataExport #%s: %s\n", $dataExport->getId(), $dataExport->getName()); + } + '/experimental/data-exports/{id}': + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Reports + tags: + - Data exports + summary: Retrieve a data export request + operationId: GetDataExport + x-sdk-operation-name: get + description: Retrieves a data export request. + parameters: + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Data export request. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/DataExport' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: JavaScript + source: >- + const queuedExport = await api.exports.get({resource: 'transaction', + id: 'foobar-001'}); + + console.log(queuedExport.fields.status); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $dataExport = $service->dataExports()->get('dataExportId'); + put: + x-products: + - Reports + tags: + - Data exports + summary: Modify a data export + operationId: PutDataExport + x-sdk-operation-name: update + description: Modifies a pending data export. + parameters: + - $ref: '#/components/parameters/collectionExpand' + requestBody: + $ref: '#/components/requestBodies/DataExport' + responses: + '200': + description: Data export modified. + content: + application/json: + schema: + $ref: '#/components/schemas/DataExport' + '201': + description: Data export updated. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/DataExport' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $dataExport = new \Rebilly\Sdk\Model\Customers([ + 'name' => 'Daily customer export v2', + 'format' => 'csv', + 'fields' => [ + 'firstName', + 'lastName', + 'email', + 'lifetimeRevenue/amount', + 'lastPaymentTime', + ], + ]); + + try { + $dataExport = $service->dataExports()->update('dataExportId', $dataExport); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + + // OR + + $dataExport = $service->dataExports()->get('dataExportId'); + $dataExport->setFields([ + 'firstName', + 'lastName', + 'email', + 'lifetimeRevenue/amount', + 'lastPaymentTime', + ]); + + try { + $dataExport = $service->dataExports()->update('dataExportId', $dataExport); + } catch (\Rebilly\Sdk\Exception\DataValidationException $e) { + print_r($e->getValidationErrors()); + } + delete: + x-products: + - Reports + tags: + - Data exports + summary: Delete a data export + operationId: DeleteDataExport + x-sdk-operation-name: delete + description: Deletes a data export. + responses: + '204': + $ref: '#/components/responses/NoContent' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $service->dataExports()->delete('dataExportId'); + /organization-exports: + get: + x-products: + - Users + tags: + - Organization data exports + summary: Retrieve organization data exports + operationId: GetOrganizationExportCollection + x-sdk-operation-name: getAll + description: Retrieves a list of organization data exports. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: List of organization data exports retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/OrganizationExport' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Users + tags: + - Organization data exports + summary: Request an organization data export + operationId: PostOrganizationExport + x-sdk-operation-name: create + description: |- + Request an organization data export. + The data export is asynchronously processed. + requestBody: + content: + application/json: + schema: + type: object + properties: + includeFiles: + description: Sets the export to include organization files. + type: boolean + default: false + example: true + responses: + '201': + description: Organization data export request received. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/OrganizationExport' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/organization-exports/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Users + tags: + - Organization data exports + summary: Retrieve an organization data export request + operationId: GetOrganizationExport + x-sdk-operation-name: get + description: >- + Retrieve an organization data export request with a specified + organization ID. + responses: + '200': + description: Organization data export request. + content: + application/json: + schema: + $ref: '#/components/schemas/OrganizationExport' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /experimental/histograms/transactions: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Histograms + summary: Retrieve transaction histogram report data + operationId: GetHistogramTransactionReport + x-sdk-operation-name: getTransactionHistogramReport + description: Retrieves transaction histogram report data. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - name: aggregationPeriod + in: query + description: Aggregation period of the report. + required: true + schema: + type: string + enum: + - hour + - day + - month + - name: metric + in: query + description: Metric on which the report is based. + required: true + schema: + type: string + enum: + - approval + - auth_approval + - avg_sales + - refunds + - refunds_count + - sales + - sales_count + - all_sales_count + - auth_approval_count + - disputes_count + - disputes_rate + - credits + - credits_count + - unapproved_count + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: Transaction report retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/HistogramData' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + const params = { + periodStart: '2017-09-21T00:00:00Z', + // seven day period + periodEnd: '2017-09-28T23:59:59Z', + aggregationField: 'website', + aggregationPeriod: 'day', + metric: 'approval' + }; + + const report = await + api.histograms.getTransactionHistogramReport(params); + + console.log(report.fields.data); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $histogram = $service->histograms()->getTransactionHistogramReport( + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + 'website', + 'day', + 'sales', + ); + /experimental/reports/api-log-summary: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve an API log summary report + operationId: GetApiLogSummaryReport + x-sdk-operation-name: getApiLogSummary + description: Retrieves an API log summary report. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Report retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ApiLogSummary' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + periodStart: '2017-09-21T00:00:00Z', + periodEnd: '2017-09-28T23:59:59Z', + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getApiLogSummary(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getApiLogSummary( + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + limit: 5, + ); + /experimental/reports/cumulative-subscriptions: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a cumulative subscriptions report + operationId: GetCumulativeSubscriptionReport + x-sdk-operation-name: getCumulativeSubscriptions + description: Retrieves a cumulative subscriptions report. + parameters: + - name: aggregationField + in: query + description: Report aggregation field. + required: true + schema: + type: string + enum: + - day + - month + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: Report retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/CumulativeSubscriptions' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + aggregationField: 'day', + periodStart: '2017-09-21T00:00:00Z', + periodEnd: '2017-09-28T23:59:59Z', + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getCumulativeSubscriptions(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getCumulativeSubscriptions( + 'day', + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + limit: 5, + ); + /experimental/reports/dashboard: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve dashboard data + operationId: GetDashboardReport + x-sdk-operation-name: getDashboardMetrics + description: Retrieves dashboard data. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - name: metrics + in: query + description: Comma-separated list of metrics. + schema: + type: string + - name: segments + in: query + description: Dashboard report segments as a JSON array. + schema: + type: string + responses: + '200': + description: Report retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/DashboardResponse' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $dashboard = $service->reports()->getDashboardMetrics( + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + metrics: 'approvalRate,salesCount,salesValue,refundsValue', + ); + /experimental/reports/dcc-markup: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a DCC markup report + operationId: GetDccMarkupReport + x-sdk-operation-name: getDccMarkup + description: Retrieves a Dynamic Currency Conversion (DCC) markup report. + parameters: + - name: aggregationField + in: query + description: Report aggregation field. + required: true + schema: + type: string + enum: + - day + - month + - bin + - country + - baseCurrency + - quoteCurrency + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/DccMarkup' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + aggregationField: 'day', + periodStart: '2017-09-21T00:00:00Z', + periodEnd: '2017-09-28T23:59:59Z', + limit: 20, + offset: 0, + filter: `gatewayAccounts:f9b4fa10-df1d-48a3-85b3-ff6bd7ce0ed2; \ + transactionResult:approved,canceled,declined,unknown`, + tz: 0 + }; + const report = await api.reports.getDccMarkup(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getDccMarkup( + 'day', + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + limit: 5, + ); + /experimental/reports/declined-transactions: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a declined transactions report + operationId: GetDeclinedTransactionsReport + x-sdk-operation-name: getDeclinedTransactions + description: Retrieves a report on declined transactions. + parameters: + - name: aggregationField + in: query + description: Report aggregation field. + required: true + schema: + type: string + enum: + - gatewayResponseMessage + - gatewayResponseOriginalMessage + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: Report retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ReportDeclinedTransactions' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + /experimental/reports/disputes: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a disputes report + operationId: GetDisputeReport + x-sdk-operation-name: getDisputes + description: Retrieves a disputes report. + parameters: + - name: aggregationField + in: query + description: Report aggregation field. + required: true + schema: + type: string + enum: + - website + - gatewayAcquirer + - currency + - bin + - country + - rebillNumber + - retryNumber + - gatewayAccount + - transactionAmount + - name: periodMonth + in: query + description: Report month in `YYYY-MM` format. + required: true + schema: + type: string + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportDisputes' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + aggregationField: 'website', + periodMonth: '2017-09', + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getDisputes(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getDisputes( + 'website', + '2022-01', + limit: 5, + ); + /experimental/reports/events-triggered: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve events triggered summary report + operationId: GetTriggeredEventReport + x-sdk-operation-name: getEventsTriggeredSummary + description: Retrieves an events triggered summary report. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportEventsTriggeredSummary' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + periodStart: '2017-09-22T00:00:00Z', + periodEnd: '2017-09-29T23:59:59Z', + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getEventsTriggeredSummary(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getEventsTriggeredSummary( + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + limit: 5, + ); + '/experimental/reports/events-triggered/{eventType}/rules': + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + parameters: + - name: eventType + in: path + required: true + description: Type of system event. + schema: + $ref: '#/components/schemas/EventType' + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a rules matched summary report + operationId: GetTriggeredEventRuleReport + x-sdk-operation-name: getTriggeredEventRuleReport + description: >- + Retrieves a rules matched summary report that is based on triggered + events. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportRulesMatchedSummary' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + periodStart: '2017-09-22T00:00:00Z', + periodEnd: '2017-09-29T23:59:59Z', + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getRulesMatchedSummary(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getTriggeredEventRuleReport( + 'account-password-reset-requested', + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + limit: 5, + ); + /experimental/reports/future-renewals: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a future renewals report + operationId: GetFutureRenewalReport + x-sdk-operation-name: getFutureRenewals + description: Retrieves a future renewals report. + parameters: + - name: periodStart + in: query + description: |- + Date and time when the report starts. + This date must be in the future. + required: true + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2032-01 + - name: periodEnd + in: query + description: |- + Date and time when the report ends. + This date must be in the future. + required: true + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2032-02 + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/FutureRenewals' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + periodStart: '2017-09-22T00:00:00Z', + periodEnd: '2017-09-29T23:59:59Z', + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getFutureRenewals(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getFutureRenewals( + '2022-10', + '2022-11', + limit: 5, + ); + /experimental/reports/journal: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a journal report + operationId: GetJournalReport + x-sdk-operation-name: getJournal + description: >- + Retrieves a journal report which displays revenue for each account. + + + A revenue journal is a detailed revenue waterfall report that describes + revenue which is recognized at a certain month, + + aggregated by product ID, product accounting code, or plan ID. + + It contains information on booked revenue, + + recognized revenue for the aggregation field in the booked period, + + and the remaining revenue up to the selected month. + parameters: + - name: currency + in: query + description: Revenue currency. + required: true + schema: + $ref: '#/components/schemas/CurrencyCode' + - name: bookedFrom + in: query + description: >- + Year and month from which revenue is booked. + + If this value is omitted, booked revenue is recorded from the first + booked amount. + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-01 + - name: bookedTo + in: query + description: >- + Year and month in which revenue is booked until. + + If this value is omitted, booked revenue is recorded until the most + recently booked amount. + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-01 + - name: recognizedAt + in: query + description: Year and month when revenue is recognized. + required: true + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-04 + - name: aggregationField + in: query + description: Report aggregation field. + required: true + schema: + type: string + enum: + - product.accountingCode + - product.id + - plan.id + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportJournal' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + currency: 'USD', + bookedFrom: '2022-01', + bookedTo: '2022-06', + recognizedAt: '2022-06', + aggregationField: 'product.accountingCode', + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getJournal(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getJournal( + 'USD', + '2022-04', + 'product.id', + limit: 5, + ); + /experimental/reports/kyc-acceptance-summary: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a KYC acceptance summary report + operationId: GetKycAcceptanceSummaryReport + x-sdk-operation-name: getKycAcceptanceSummary + description: Retrieves a Know Your Customer (KYC) acceptance summary report. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + responses: + '200': + description: Report retrieved. + content: + application/json: + schema: + type: object + title: GetKycAcceptanceSummaryResponse + properties: + data: + description: KYC acceptance data. + type: array + items: + type: object + description: Amount of accepted KYC documents. + readOnly: true + properties: + documentType: + allOf: + - description: Type of KYC document. + example: address-proof + - $ref: '#/components/schemas/KycDocumentTypes' + statusStatistics: + type: object + description: Statistics for each status. + properties: + accepted: + type: object + description: Statistics for the `accepted` status. + properties: + total: + type: integer + description: Total amount of accepted documents. + example: 6 + automatically: + type: integer + description: >- + Amount of documents that have been accepted + automatically. + example: 4 + manually: + type: integer + description: >- + Amount of documents that have been accepted + by reviewer. + example: 2 + afterAutoRejected: + type: integer + description: >- + Amount of documents that have been rejected + automatically and then accepted by reviewer. + example: 2 + rejected: + type: object + description: Statistics for the `rejected` status. + properties: + total: + type: integer + description: Total amount of rejected documents. + example: 2 + automatically: + type: integer + description: >- + Amount of documents that have been rejected + automatically. + example: 1 + manually: + type: integer + description: >- + Amount of documents that have been rejected + by reviewer. + example: 1 + afterAutoAccepted: + type: integer + description: >- + Amount of documents that have been accepted + automatically and then rejected by reviewer. + example: 1 + pending: + type: object + description: Statistics for the `pending` status. + properties: + total: + type: integer + description: Total amount of pending documents. + example: 0 + archived: + type: object + description: Statistics for the `archived` status. + properties: + total: + type: integer + description: Total amount of archived documents. + example: 0 + total: + type: integer + description: Total amount of documents. + example: 8 + accuracyRate: + type: number + description: >- + Percentage of automatically accepted and rejected + documents that remain in the same status after + review. + + Calculated based on the following: `(non-archived + total - accepted auto rejected - rejected auto + accepted)/non-archived total`. + example: 62.5 + acceptanceRate: + type: number + description: >- + Percentage of accepted documents from the total + number of KYC documents. + + Calculated based on the following: `accepted total / + total of non-archived documents`. + example: 75 + manualReviewTime: + type: number + description: >- + Median duration in minutes between review and + created time for manually reviewed documents. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getKycAcceptanceSummary( + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + ); + /experimental/reports/kyc-rejection-summary: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a KYC rejections report + operationId: GetKycRejectionSummaryReport + x-sdk-operation-name: getKycRejectionSummary + description: >- + Retrieves a Know Your Customer (KYC) rejection report by type and by + rejection reasons. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportKycRejections' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getKycRejectionSummary( + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + ); + /experimental/reports/kyc-request-summary: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a KYC requests report + operationId: GetKycRequestSummaryReport + x-sdk-operation-name: getKycRequestSummary + description: >- + Retrieves a Know Your Customer (KYC) request report by type and by + rejection reasons. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportKycRequests' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getKycRequestSummary( + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + ); + /experimental/reports/monthly-recurring-revenue: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a MRR report + operationId: GetMonthlyRecurringRevenueReport + x-sdk-operation-name: getMonthlyRecurringRevenue + description: >- + Retrieves a Monthly Recurring Revenue (MRR) report. + + + Use MRR reports to view information on the predictable recurring revenue + for your business over a period of months. + parameters: + - name: currency + in: query + description: Revenue currency. + required: true + schema: + $ref: '#/components/schemas/CurrencyCode' + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-01 + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-06 + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportMonthlyRecurringRevenue' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const report = await api.reports.getMonthlyRecurringRevenue({ + periodStart: '2016-09', + periodEnd: '2017-09', + limit: 20, + offset: 0, + tz: 0 + }); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getMonthlyRecurringRevenue( + 'USD', + '2022-01', + '2022-03', + limit: 5, + ); + /experimental/reports/renewal-sales: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a renewal sales report + operationId: GetRenewalSaleReport + x-sdk-operation-name: getRenewalSales + description: Retrieves a renewal sales report. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-01 + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-02 + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/RenewalSales' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + periodStart: '2017-09', + periodEnd: '2017-09', + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getRenewalSales(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getRenewalSales( + '2022-01', + '2022-03', + limit: 5, + ); + /experimental/reports/retention-percentage: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a retention percentage report + operationId: GetRetentionPercentageReport + x-sdk-operation-name: getRetentionPercentage + description: Retrieves a retention percentage report. + parameters: + - name: aggregationField + in: query + description: Report aggregation field. + required: true + schema: + type: string + enum: + - day + - month + - quarter + - year + - name: aggregationPeriod + in: query + description: Report aggregation period. + required: true + schema: + type: string + enum: + - day + - month + - quarter + - year + - cycle + - name: includeSwitchedSubscriptions + in: query + description: Specifies whether to include switched subscriptions. + schema: + type: string + enum: + - 'true' + - 'false' + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionCriteria' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportRetentionPercentage' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + aggregationField: 'month', + aggregationPeriod: 'month', + periodStart: '2016-09-01T00:00:00Z', + periodEnd: '2017-09-01T00:00:00Z', + includeSwitchedSubscriptions: false, + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getRetentionPercentage(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getRetentionPercentage( + 'day', + 'month', + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + limit: 5, + ); + /experimental/reports/retention-value: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a retention value report + operationId: GetRetentionValueReport + x-sdk-operation-name: getRetentionValue + description: >- + Retrieves the total number of new customers for each aggregation period + and the corresponding value for each customer over time. + parameters: + - name: aggregationField + in: query + description: Report aggregation field. + required: true + schema: + type: string + enum: + - day + - month + - quarter + - year + - leadsSource + - leadsMedium + - leadsCampaign + - leadsContent + - leadsTerm + - leadsAffiliate + - leadsSubAffiliate + - leadsSalesAgent + - bin + - name: aggregationPeriod + in: query + description: Report aggregation period. + required: true + schema: + type: string + enum: + - day + - month + - quarter + - year + - name: includeRefunds + in: query + description: Specifies whether to include refunds. + schema: + type: string + enum: + - 'true' + - 'false' + - name: includeDisputes + in: query + description: Specifies whether to include disputes. + schema: + type: string + enum: + - 'true' + - 'false' + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionCriteria' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportRetentionValue' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + aggregationField: 'month', + aggregationPeriod: 'month', + periodStart: '2016-09-01T00:00:00Z', + periodEnd: '2017-09-01T00:00:00Z', + includeRefunds: true, + includeDisputes: false, + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getRetentionValue(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getRetentionValue( + 'month', + 'day', + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + limit: 5, + ); + /experimental/reports/revenue-waterfall: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a revenue waterfall report + operationId: GetRevenueWaterfallReport + x-sdk-operation-name: getRevenueWaterfall + description: >- + Retrieves a revenue waterfall report. + + + Use revenue waterfall reports to view revenue that is recognized up to a + given month. + + Revenue waterfall reports contain information on booked revenue, + + recognized revenue for the months in the issued period, + + and the remaining revenue up to the specified month. + parameters: + - name: currency + in: query + description: Revenue currency. + required: true + schema: + $ref: '#/components/schemas/CurrencyCode' + - name: issuedFrom + in: query + description: Date from which revenue invoice is issued. + required: true + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-01 + - name: issuedTo + in: query + description: Date to which revenue invoice is issued. + required: true + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-04 + - name: recognizedTo + in: query + description: Month up to which revenue is recognized. + required: true + schema: + type: string + pattern: '^\d{4}-\d{2}$' + example: 2022-04 + responses: + '200': + description: Report retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ReportRevenueWaterfall' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const report = await api.reports.getRevenueWaterfall({ + issuedFrom: '2016-09', + issuedTo: '2017-09', + recognizedTo: '2017-09', + limit: 20, + offset: 0, + tz: 0 + }); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getRevenueWaterfall( + 'USD', + '2022-01', + '2022-02', + '2022-02', + ); + /experimental/reports/revenue-audit: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a revenue audit report + operationId: GetRevenueAuditReport + x-sdk-operation-name: getRevenueAudit + description: >- + Retrieves a revenue audit report which contains detailed journal + entries. + + + A revenue audit report is a combination of a granular [revenue + waterfall](https://www.rebilly.com/docs/data-tables/revenue-recognition/#revenue-waterfall) + and a journal report, + + which describes revenue per account. + + Use + [`filter`](https://all-rebilly.redoc.ly/#section/Using-filter-with-collections) + `limit` and `offset` parameters to restrict the output. + security: + - SecretApiKey: [] + - JWT: [] + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Revenue audit report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/RevenueEntry' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getRevenueAudit(limit: 5); + /experimental/reports/subscription-cancellation: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a subscription cancellation report + operationId: GetSubscriptionCancellationReport + x-sdk-operation-name: getSubscriptionCancellation + description: Retrieves a subscription cancellation report. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - name: aggregationField + in: query + description: Report aggregation field. + required: true + schema: + type: string + enum: + - planId + - websiteId + - canceledBy + - cancelCategory + - leadSource.source + - leadSource.medium + - leadSource.campaign + - leadSource.content + - leadSource.term + - leadSource.affiliate + - leadSource.subAffiliate + - leadSource.salesAgent + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionCancellationReport' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: >- + const params = { + aggregationField: 'day', + periodStart: '2017-09-21T00:00:00Z', + periodEnd: '2017-09-28T23:59:59Z', + limit: 20, + offset: 0, + tz: 0 + }; + + const report = await + api.reports.getSubscriptionCancellation(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getSubscriptionCancellation( + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + 'leadSource.source', + limit: 5, + ); + /experimental/reports/subscription-renewal: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a subscription renewal report + operationId: GetSubscriptionRenewalReport + x-sdk-operation-name: getSubscriptionRenewal + description: Retrieves a subscription renewal report. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionRenewal' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + periodStart: '2017-09-21T00:00:00Z', + periodEnd: '2017-09-28T23:59:59Z', + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getSubscriptionRenewal(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getSubscriptionRenewal( + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + limit: 5, + ); + /experimental/reports/tax: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a tax report + operationId: GetTaxReport + x-sdk-operation-name: getTax + description: |- + Retrieves a tax report, which displays taxes collected on sales. + Tax amounts are aggregated by state and municipality. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - name: accountingMethod + in: query + description: Accounting method to use when generating the report. + required: true + schema: + type: string + enum: + - cash + - accrual + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportTax' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: | + const params = { + periodStart: '2023-01-01 00:00-00', + periodEnd: '2023-03-31 23:59:59', + accountingMethod: 'cash', + limit: 20, + offset: 0, + }; + const report = await api.reports.getTax(params); + /experimental/reports/time-series-transaction: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a time series transactions report + operationId: GetTimeSeriesTransactionReport + x-sdk-operation-name: getTimeSeriesTransaction + description: Retrieves a transactions report that is aggregated by time periods. + parameters: + - name: type + in: query + description: Type of report aggregation. + required: true + schema: + type: string + enum: + - count + - amount + - approval-rate + - incomplete-rate + - name: subaggregate + in: query + description: Report subaggregate. + required: true + schema: + type: string + enum: + - website + - gateway-account + - currency + - plan + - leads.source + - leads.medium + - leads.campaign + - leads.content + - leads.term + - leads.affiliate + - leads.subaffiliate + - leads.sales-agent + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/TimeSeriesTransaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + periodStart: '2017-08-29T00:00:00Z', + periodEnd: '2017-09-29T23:59:59Z', + type: 'count', + subaggregate: 'gateway-account', + limit: 20, + offset: 0 + }; + const report = await api.reports.getTimeSeriesTransaction(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getTimeSeriesTransaction( + 'approval-rate', + 'leads.sales-agent', + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + ); + /experimental/reports/transactions-time-dispute: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a transaction time dispute report + operationId: GetTransactionTimeDisputeReport + x-sdk-operation-name: getTransactionsTimeDispute + description: >- + Retrieves a dispute delay in days report. + + This report describes the amount of time between a transaction and a + dispute. + parameters: + - name: aggregationField + in: query + description: Report aggregation field. + required: true + schema: + type: string + enum: + - website + - processor + - currency + - bin + - country + - rebillNumber + - retryNumber + - gatewayAccount + - transactionAmount + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportDisputeDelays' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + aggregationField: 'website', + periodStart: '2017-09-22T00:00:00Z', + periodEnd: '2017-09-29T23:59:59Z', + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getTransactionsTimeDispute(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getTransactionsTimeDispute( + 'rebillNumber', + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + limit: 5, + ); + /experimental/reports/transactions: + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Reports + summary: Retrieve a transactions report + operationId: GetTransactionReport + x-sdk-operation-name: getTransactions + description: Retrieves a transactions report. + parameters: + - name: periodStart + in: query + description: Date and time when the report starts. + required: true + schema: + type: string + format: date-time + - name: periodEnd + in: query + description: Date and time when the report ends. + required: true + schema: + type: string + format: date-time + - name: aggregationField + in: query + description: Report aggregation field. + required: true + schema: + type: string + enum: + - website + - currency + - bin + - rebillNumber + - transactionResult + - transactionType + - gatewayAccount + - gateway + - retryNumber + - plan + - cardBrand + - leadSource.source + - leadSource.medium + - leadSource.campaign + - leadSource.content + - leadSource.term + - leadSource.affiliate + - leadSource.subAffiliate + - leadSource.salesAgent + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + responses: + '200': + description: Report retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/ReportTransactions' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: JavaScript + source: |- + const params = { + aggregationField: 'website', + periodStart: '2017-09-22T00:00:00Z', + periodEnd: '2017-09-29T23:59:59Z', + limit: 20, + offset: 0, + tz: 0 + }; + const report = await api.reports.getTransactions(params); + - lang: PHP + source: |- + $service = new \Rebilly\Sdk\ReportsService($client); + + $report = $service->reports()->getTransactions( + new \DateTimeImmutable('2022-01-01'), + new \DateTimeImmutable('now'), + 'leadSource.subAffiliate', + limit: 5, + ); + '/experimental/subscriptions/{subscriptionId}/summary-metrics': + servers: + - url: >- + https://api-sandbox.rebilly.com/experimental/organizations/{organizationId} + description: Sandbox server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + - url: 'https://api.rebilly.com/experimental/organizations/{organizationId}' + description: Live server + variables: + organizationId: + default: unknown + description: >- + Unique organization identifier. + + An organization is an entity that represents a company. + + For more information, see [Obtain an organization + ID](https://www.rebilly.com/docs/settings/organizations-and-websites/#obtain-your-organization-id-and-website-id). + parameters: + - name: subscriptionId + in: path + required: true + description: ID of the subscription order. + schema: + type: string + get: + x-badge: Experimental + x-products: + - Reports + tags: + - Orders + summary: Retrieve subscription order summary metrics + operationId: GetSubscriptionSummaryMetricReport + x-sdk-operation-name: getSubscriptionSummaryMetrics + description: Retrieves summary metrics for a subscription order with a specified ID. + responses: + '200': + description: Metrics retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionSummaryMetrics' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + x-codeSamples: + - lang: PHP + source: >- + $service = new \Rebilly\Sdk\ReportsService($client); + + + $report = + $service->subscriptions()->getSubscriptionSummaryMetrics('subscriptionId'); + /storefront/account: + get: + x-products: + - Storefront + tags: + - Account + summary: Retrieve Account + operationId: StorefrontGetAccount + x-sdk-operation-name: get + security: + - CustomerJWT: [] + description: Retrieve account. + parameters: + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Account retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + patch: + x-products: + - Storefront + tags: + - Account + summary: Update Account + operationId: StorefrontPatchAccount + x-sdk-operation-name: update + security: + - CustomerJWT: [] + description: Register account. + requestBody: + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/StorefrontAccount' + responses: + '200': + description: Account updated. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /storefront/account/forgot-password: + post: + x-products: + - Storefront + tags: + - Password reset + summary: Request a password reset + operationId: StorefrontPostForgotPassword + x-sdk-operation-name: requestPasswordReset + security: + - PublishableApiKey: [] + description: Sends an email with a link containing a token to reset a user password. + requestBody: + content: + application/json: + schema: + type: object + required: + - username + properties: + username: + description: >- + Username of the account to which a verification email is + sent. + type: string + responses: + '204': + description: Email sent. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /storefront/account/password: + patch: + x-products: + - Storefront + tags: + - Account + summary: Change an account password + operationId: StorefrontPatchAccountPassword + x-sdk-operation-name: changePassword + security: + - CustomerJWT: [] + description: Changes an account password. + requestBody: + $ref: '#/components/requestBodies/PatchAccountPassword' + responses: + '200': + description: Account password updated. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /storefront/account/resend-verification: + post: + x-products: + - Storefront + tags: + - Email verification + summary: Resend email verification + operationId: StorefrontPostAccountResendVerification + x-sdk-operation-name: resendEmailVerification + security: + - PublishableApiKey: [] + description: Resends a verification email for an account with a specified username. + requestBody: + content: + application/json: + schema: + type: object + required: + - username + properties: + username: + description: >- + Username of the account to which a verification email is + sent. + type: string + responses: + '204': + description: Email sent. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/account/reset-password/{token}': + parameters: + - $ref: '#/components/parameters/token' + post: + x-products: + - Storefront + tags: + - Password reset + summary: Finish password reset + operationId: StorefrontPostResetPassword + x-sdk-operation-name: confirmPasswordReset + security: + - PublishableApiKey: [] + description: Resets a user's account password. + requestBody: + content: + application/json: + schema: + type: object + required: + - newPassword + properties: + newPassword: + type: string + example: newP@$$w0rd + description: New user account password. + responses: + '201': + description: Password reset. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/account/verification/{token}': + parameters: + - $ref: '#/components/parameters/token' + post: + x-products: + - Storefront + tags: + - Email verification + summary: Verify an account email + operationId: StorefrontPostAccountVerification + x-sdk-operation-name: verifyEmail + security: + - PublishableApiKey: [] + description: Verifies an account email. + responses: + '204': + description: Account verified. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/billing-portals/{slug}': + parameters: + - name: slug + in: path + description: Resource slug. + required: true + schema: + type: string + maxLength: 100 + pattern: '^[@~\-\.\w]+$' + get: + x-products: + - Storefront + tags: + - Billing portals + summary: Retrieve a billing portal + operationId: StorefrontGetBillingPortal + x-sdk-operation-name: get + description: Retrieve a billing portal with a specified slug. + security: [] + responses: + '200': + description: Billing portal retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontBillingPortal' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/storefront/deposit-strategies/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + x-badge: Experimental + tags: + - Deposits + summary: Retrieve a deposit strategy + operationId: StorefrontGetDepositStrategy + x-sdk-operation-name: get + description: Retrieves a deposit strategy with a specified ID. + responses: + '200': + description: Deposit strategy retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/DepositStrategy' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/storefront/deposit-requests/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + x-badge: Experimental + tags: + - Deposits + summary: Retrieve a deposit request + operationId: StorefrontGetDepositRequest + x-sdk-operation-name: get + security: + - CustomerJWT: [] + description: Retrieves a deposit request with a specified ID. + parameters: + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Deposit request retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/DepositRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /storefront/deposit: + post: + x-products: + - Storefront + x-badge: Experimental + tags: + - Deposits + summary: Create a deposit + operationId: StorefrontPostDeposit + x-sdk-operation-name: create + security: + - CustomerJWT: [] + description: Creates a deposit transaction. + requestBody: + $ref: '#/components/requestBodies/PostDeposit' + responses: + '201': + description: Transaction created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontTransaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/checkout-forms/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - Checkout forms + summary: Retrieve a checkout form + operationId: StorefrontGetCheckoutForm + x-sdk-operation-name: get + description: Retrieves a checkout form with a specified ID. + security: [] + responses: + '200': + description: Checkout form retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontCheckoutForm' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/storefront/custom-fields/{resource}': + parameters: + - $ref: '#/components/parameters/storefrontCustomFieldResource' + get: + x-products: + - Storefront + tags: + - Custom fields + summary: Retrieve custom fields + operationId: StorefrontGetCustomFieldCollection + x-sdk-operation-name: getAll + security: + - CustomerJWT: [] + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + description: Retrieves the custom fields schema for a specified resource. + responses: + '200': + description: Schema of custom fields retrieved. + content: + application/json: + schema: + description: List of custom fields. + type: array + items: + $ref: '#/components/schemas/CustomField' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + /storefront/invoices: + get: + x-products: + - Storefront + tags: + - Invoices + summary: Retrieve invoices + operationId: StorefrontGetInvoiceCollection + x-sdk-operation-name: getAll + security: + - CustomerJWT: [] + description: Retrieve a list of invoices. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: Invoices retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/StorefrontInvoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/storefront/invoices/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - Invoices + summary: Retrieve a Invoice + operationId: StorefrontGetInvoice + x-sdk-operation-name: get + security: + - CustomerJWT: [] + description: Retrieves an invoice with a specified ID. + responses: + '200': + description: Invoice retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontInvoice' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + /storefront/kyc-documents: + get: + x-products: + - Storefront + tags: + - KYC documents + summary: Retrieve KYC documents + operationId: StorefrontGetKycDocumentCollection + x-sdk-operation-name: getAll + security: + - CustomerJWT: [] + description: Retrieves a list of KYC documents. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: List of KYC documents retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/StorefrontKycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Storefront + tags: + - KYC documents + summary: Create a KYC document + operationId: StorefrontPostKycDocument + x-sdk-operation-name: create + security: + - CustomerJWT: [] + description: Creates a KYC document. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontKycDocument' + description: KYC document resource. + required: true + responses: + '201': + description: KYC document created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontKycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/kyc-documents/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - KYC documents + summary: Retrieve a KYC Document + operationId: StorefrontGetKycDocument + x-sdk-operation-name: get + security: + - CustomerJWT: [] + description: Retrieves a KYC document with a specified ID. + responses: + '200': + description: KYC document retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontKycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + patch: + x-products: + - Storefront + tags: + - KYC documents + summary: Update a KYC document + operationId: StorefrontPatchKycDocument + x-sdk-operation-name: update + security: + - CustomerJWT: [] + description: Updates a KYC document with a specified ID. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontKycDocument' + description: KYC document resource. + required: true + responses: + '200': + description: KYC document updated. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontKycDocument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/kyc-requests/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - KYC documents + summary: Retrieve a KYC request + operationId: StorefrontGetKycRequest + x-sdk-operation-name: get + security: + - CustomerJWT: [] + description: Retrieves a KYC request with a specified ID. + parameters: + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: KYC request retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontKycRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /storefront/kyc-liveness-sessions: + post: + x-products: + - Storefront + tags: + - KYC documents + summary: Create a KYC liveness session + operationId: StorefrontPostKycLivenessSession + x-sdk-operation-name: create + description: Creates a KYC liveness session. + security: + - CustomerJWT: [] + requestBody: + $ref: '#/components/requestBodies/PostKycLivenessSession' + description: KYC request resource. + required: true + responses: + '201': + description: KYC liveness session created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontKycLivenessSession' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/kyc-liveness-sessions/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - KYC documents + summary: Retrieve a KYC liveness session + operationId: StorefrontGetKycLivenessSession + x-sdk-operation-name: get + description: Retrieves a KYC liveness session with a specified ID. + security: + - CustomerJWT: [] + responses: + '200': + description: KYC liveness session retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontKycLivenessSession' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/storefront/kyc-liveness-sessions/{id}/finish': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Storefront + tags: + - KYC documents + summary: Finish KYC liveness session + operationId: StorefrontPostKycLivenessSessionFinish + x-sdk-operation-name: finish + description: Attempt to finalize a pending KYC liveness session. + security: + - CustomerJWT: [] + responses: + '200': + description: KYC liveness session finalization attempt successful. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontKycLivenessSession' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + /storefront/login: + post: + x-products: + - Storefront + tags: + - Authentication + summary: Create a session with username and password + operationId: StorefrontPostLogin + x-sdk-operation-name: login + security: + - PublishableApiKey: [] + description: Creates a session with a specified username and password. + requestBody: + content: + application/json: + schema: + type: object + required: + - username + - password + properties: + username: + description: User's username. + type: string + password: + description: User's current password. + type: string + responses: + '201': + description: Logged into account. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontCustomerJWT' + '401': + $ref: '#/components/responses/Unauthorized' + /storefront/logout: + post: + x-products: + - Storefront + tags: + - Authentication + summary: Destroys the user's current session + operationId: StorefrontPostLogout + x-sdk-operation-name: logout + security: + - CustomerJWT: [] + description: Destroys the user's current session. + responses: + '204': + description: Account logged out. + '401': + $ref: '#/components/responses/Unauthorized' + /storefront/orders: + get: + x-products: + - Storefront + tags: + - Orders + summary: Retrieve orders + operationId: StorefrontGetOrderCollection + x-sdk-operation-name: getAll + security: + - CustomerJWT: [] + description: Retrieve a list of orders. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of orders retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/StorefrontOrder' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/storefront/orders/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - Orders + summary: Retrieve an order + operationId: StorefrontGetOrder + x-sdk-operation-name: get + security: + - CustomerJWT: [] + description: Retrieve an order with a specified ID. + responses: + '200': + description: Order retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontOrder' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + patch: + x-products: + - Storefront + tags: + - Orders + summary: Update an order + operationId: StorefrontPatchOrder + x-sdk-operation-name: update + security: + - CustomerJWT: [] + description: Updates an order with a specified ID. + requestBody: + $ref: '#/components/requestBodies/PatchOrder' + responses: + '200': + description: Order updated. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontOrder' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/orders/{id}/cancellation': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Storefront + tags: + - Orders + summary: Cancel an order + operationId: StorefrontPostOrderCancellation + x-sdk-operation-name: cancel + security: + - CustomerJWT: [] + description: Cancels an order with a specified ID. + requestBody: + $ref: '#/components/requestBodies/PostOrderCancellation' + responses: + '201': + description: Order canceled. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontOrder' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/orders/{id}/pause': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Storefront + tags: + - Orders + summary: Pause a subscription order + operationId: StorefrontPostOrderPause + x-sdk-operation-name: pause + security: + - CustomerJWT: [] + description: Pauses an order with a specified ID. + requestBody: + $ref: '#/components/requestBodies/PostOrderPause' + responses: + '201': + description: Order paused. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontOrder' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + /storefront/payment: + post: + x-products: + - Storefront + tags: + - Purchases + summary: Perform a payment + operationId: StorefrontPostPayment + x-sdk-operation-name: payment + security: + - CustomerJWT: [] + - PublishableApiKey: [] + description: >- + Performs a payment for a transaction or an invoice. + + + If the customer's JSON Web Token (JWT) contains a `transactionId`, or + `invoiceId` value, + + these values are used instead of the JWT value. + + The `transactionId` has a higher priority than the `invoiceId`. + requestBody: + $ref: '#/components/requestBodies/PostPayment' + responses: + '200': + description: Transaction created. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontTransaction' + '201': + description: Transaction updated. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontTransaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/ValidationError' + /storefront/payment-instruments: + get: + x-products: + - Storefront + tags: + - Payment instruments + summary: Retrieve payment instruments + operationId: StorefrontGetPaymentInstrumentCollection + x-sdk-operation-name: getAll + security: + - CustomerJWT: [] + description: Retrieves a list of payment instruments. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of payment instruments retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/StorefrontPaymentInstrument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Storefront + tags: + - Payment instruments + summary: Create a payment instrument + operationId: StorefrontPostPaymentInstrument + x-sdk-operation-name: create + security: + - CustomerJWT: [] + - PublishableApiKey: [] + description: >- + Creates a payment instrument. + + + To use this operation, + + you must first create a payment token using + [FramePay](https://www.rebilly.com/docs/dev-docs/framepay/), + + then provide the token ID in this operation. + requestBody: + $ref: '#/components/requestBodies/StorefrontPostPaymentInstrument' + responses: + '201': + description: Payment instrument created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontPaymentInstrument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/payment-instruments/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - Payment instruments + summary: Retrieve a payment instrument + operationId: StorefrontGetPaymentInstrument + x-sdk-operation-name: get + security: + - CustomerJWT: [] + description: Retrieves a payment instrument with a specified ID. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Payment instrument retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontPaymentInstrument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + patch: + x-products: + - Storefront + tags: + - Payment instruments + summary: Update payment instrument + operationId: StorefrontPatchPaymentInstrument + x-sdk-operation-name: update + security: + - CustomerJWT: [] + description: >- + Updates the values of a payment instrument with a specified ID. + + + Use the Framepay payment token ID to update values. + + For more information, see + [FramePay](https://www.rebilly.com/docs/dev-docs/framepay/). + requestBody: + $ref: '#/components/requestBodies/StorefrontPatchPaymentInstrument' + responses: + '200': + description: Payment instrument updated. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontPaymentInstrument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/payment-instruments/{id}/deactivation': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Storefront + tags: + - Payment instruments + summary: Deactivate a payment instrument + operationId: StorefrontPostPaymentInstrumentDeactivation + x-sdk-operation-name: deactivate + security: + - CustomerJWT: [] + description: Deactivates a payment instrument with a specified ID. + responses: + '201': + description: Payment instrument deactivated. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontPaymentInstrument' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + x-codeSamples: + - lang: JavaScript + source: > + const paymentInstrument = await + api.paymentInstruments.deactivate({id: 'id-to-deactivate'}); + + + console.log(paymentInstrument.fields.status); + '/storefront/payment-instruments/{id}/setup': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - Payment instruments + summary: Retrieve a payment instrument setup transaction + operationId: StorefrontGetPaymentInstrumentSetup + x-sdk-operation-name: getSetupTransaction + security: + - CustomerJWT: [] + description: >- + Retrieves the latest setup transaction for a payment instrument with a + specified ID. + + + For more information, see [Transactions](#Transactions). + responses: + '200': + description: Setup transaction retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontTransaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + post: + x-products: + - Storefront + tags: + - Payment instruments + summary: Create a setup payment instrument transaction + operationId: StorefrontPostPaymentInstrumentSetup + x-sdk-operation-name: setup + security: + - CustomerJWT: [] + description: >- + Creates a setup payment instrument transaction. + + + This operation makes the payment instrument available for further + payments. + + Treat the response as a regular transaction. + + For example, approval links must be followed until the transaction is + completed. + requestBody: + $ref: '#/components/requestBodies/SetupPaymentInstrumentRequest' + responses: + '201': + description: Setup transaction created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontTransaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + /storefront/payout-requests: + get: + x-products: + - Storefront + tags: + - Transactions + summary: Retrieve payout requests + operationId: StorefrontGetPayoutRequestCollection + x-sdk-operation-name: getAll + security: + - CustomerJWT: [] + description: Retrieves a list of payout requests. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of payout requests retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/StorefrontPayoutRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/storefront/payout-requests/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - Transactions + summary: Retrieve a payout request + operationId: StorefrontGetPayoutRequest + x-sdk-operation-name: get + security: + - CustomerJWT: [] + description: Retrieves a payout request with a specified ID. + responses: + '200': + description: Payout request retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontPayoutRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + patch: + x-products: + - Storefront + tags: + - Transactions + summary: Update a payout request + operationId: StorefrontPatchPayoutRequest + x-sdk-operation-name: update + security: + - CustomerJWT: [] + description: Update a payout request with a specified ID. + requestBody: + content: + application/json: + schema: + type: object + required: + - paymentInstrumentId + properties: + paymentInstrumentId: + type: string + description: >- + ID of the preferable payment instrument for the payout + request. + maxLength: 50 + example: inst_0YVB8KPKNXCBR9EDX7JHSED75N + responses: + '200': + description: Payout request updated. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontPayoutRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + /storefront/plans: + get: + x-products: + - Storefront + tags: + - Plans + summary: Retrieve a list of plans + operationId: StorefrontGetPlanCollection + x-sdk-operation-name: getAll + description: Retrieves a list of plans. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: List of plans retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/StorefrontPlan' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/storefront/plans/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - Plans + summary: Retrieve a plan + operationId: StorefrontGetPlan + x-sdk-operation-name: get + description: Retrieves a plan with a specified ID. + parameters: + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Plan retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontPlan' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /storefront/products: + get: + x-products: + - Storefront + tags: + - Products + summary: Retrieve products + operationId: StorefrontGetProductCollection + x-sdk-operation-name: getAll + description: Retrieves a list of products. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of products retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/StorefrontProduct' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/storefront/products/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - Products + summary: Retrieve a product + operationId: StorefrontGetProduct + x-sdk-operation-name: get + description: Retrieves a product with a specified ID. + responses: + '200': + description: Product retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontProduct' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /storefront/purchase: + post: + x-products: + - Storefront + tags: + - Purchases + summary: Make a purchase + operationId: StorefrontPostPurchase + x-sdk-operation-name: purchase + description: >- + Executes a purchase. + + + A purchase can be completed both with and without authentication. + + Purchases that use a pre-created payment instrument must use + authentication. + + + To preview the purchase before completing it, + + use the [Purchase preview](./StorefrontPostPreviewPurchase) operation. + security: + - CustomerJWT: [] + - PublishableApiKey: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontPurchase' + responses: + '201': + description: Order created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontPurchase' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /storefront/preview-purchase: + post: + x-badge: Experimental + x-products: + - Storefront + tags: + - Purchases + summary: Preview a purchase + operationId: StorefrontPostPreviewPurchase + x-sdk-operation-name: preview + description: >- + Previews a purchase. + + + Use this operation to preview a purchase before completing it. + + + A purchase can be completed both with and without authentication. + + Purchases that use a pre-created payment instrument must use + authentication. + + + > **Warning:** The shipping aspect of this API is experimental and may + change to support multiple shipping methods. + security: + - CustomerJWT: [] + - PublishableApiKey: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontOrderPreview' + responses: + '200': + description: Purchase preview retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontOrderPreview' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/storefront/quotes/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - Quotes + summary: Retrieve a quote + operationId: StorefrontGetQuote + x-sdk-operation-name: get + security: + - CustomerJWT: [] + description: Retrieves a quote with a specified ID. + parameters: + - $ref: '#/components/parameters/mediaTypeJsonPdf' + - $ref: '#/components/parameters/collectionExpand' + responses: + '200': + description: Quote retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontQuote' + application/pdf: + schema: + $ref: '#/components/schemas/StorefrontQuote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/storefront/quotes/{id}/accept': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Storefront + tags: + - Quotes + summary: Accept a quote + operationId: StorefrontPostQuoteAcceptance + x-sdk-operation-name: accept + security: + - CustomerJWT: [] + description: Accepts an issued quote with specified ID. + responses: + '200': + description: Quote accepted. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontQuote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '/storefront/quotes/{id}/reject': + parameters: + - $ref: '#/components/parameters/resourceId' + post: + x-products: + - Storefront + tags: + - Quotes + summary: Reject a quote + operationId: StorefrontPostQuoteRejection + x-sdk-operation-name: reject + security: + - CustomerJWT: [] + description: Rejects an issued quote with specified ID. + responses: + '200': + description: Quote rejected. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontQuote' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + /storefront/ready-to-pay: + post: + x-products: + - Storefront + tags: + - Purchases + summary: Ready to pay + operationId: StorefrontPostReadyToPay + x-sdk-operation-name: readyToPay + security: + - PublishableApiKey: [] + description: >- + Retrieves available payment methods for a specific transaction or + purchase. + + + The order in which the payment methods are displayed to the customer + must be the same as the order in the response. + + + The list of payment methods is generated from available gateway accounts + and the last matched `adjust-ready-to-pay` action on the + `ready-to-pay-requested` event. + + If no rules match for the specific request, all methods supported by the + gateway accounts are sent. + + + To invert this behavior, place an all matching rule at the end of the + `ready-to-pay-requested` event in the rules engine, + + and include an empty `paymentMethods` property for the + `adjust-ready-to-pay` action. + + + For more information, see [Update event + rules](https://all-rebilly.redoc.ly/tag/Rules#operation/PutEventRule) + and [Gateway + accounts](https://all-rebilly.redoc.ly/tag/Gateway-accounts). + + + If this operation to pay is used with a transaction-scoped JSON Web + Token (JWT), all fields are ignored except `riskMetadata`. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ReadyToPay' + responses: + '200': + description: Payment methods retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ReadyToPayMethods' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /storefront/ready-to-payout: + post: + x-products: + - Storefront + tags: + - Purchases + summary: Ready to payout + operationId: StorefrontPostReadyToPayout + x-sdk-operation-name: readyToPayout + security: + - PublishableApiKey: [] + description: >- + Retrieves available payment methods for a specific payout amount and + currency. + + + The order in which the payment methods are displayed to the customer + must be the same as the order in the response. + + + The list of payment methods is generated from available gateway accounts + and the last matched `adjust-ready-to-payout` action on the + `ready-to-payout-requested` event. + + If no rules match for the specific request, all methods supported by the + gateway accounts are sent. + + + To invert this behavior, place all matching rules at the end of the + `ready-to-payout-requested` event in the rules engine, + + and include an empty `paymentMethods` property for the + `adjust-ready-to-payout` action. + + + For more information, see [Update event + rules](https://all-rebilly.redoc.ly/tag/Rules#operation/PutEventRule) + and [Gateway + accounts](https://all-rebilly.redoc.ly/tag/Gateway-accounts). + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ReadyToPayout' + responses: + '200': + description: Payment methods retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/ReadyToPayoutMethods' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /storefront/register: + post: + x-products: + - Storefront + tags: + - Account + summary: Register Account + operationId: StorefrontPostRegister + x-sdk-operation-name: register + security: + - PublishableApiKey: [] + description: Register account. + requestBody: + $ref: '#/components/requestBodies/PostRegister' + responses: + '201': + description: Account registered. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontAccount' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /storefront/transactions: + get: + x-products: + - Storefront + tags: + - Transactions + summary: Retrieve transactions + operationId: StorefrontGetTransactionCollection + x-sdk-operation-name: getAll + security: + - CustomerJWT: [] + description: Retrieve a list of transactions. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + - $ref: '#/components/parameters/collectionSort' + responses: + '200': + description: Transactions retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/StorefrontTransaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '/storefront/transactions/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - Transactions + summary: Retrieve a transaction + operationId: StorefrontGetTransaction + x-sdk-operation-name: get + security: + - CustomerJWT: [] + description: Retrieves a transaction with a specified ID. + responses: + '200': + description: Transaction retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontTransaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/storefront/transactions/{id}/{token}/continue': + parameters: + - $ref: '#/components/parameters/resourceId' + - $ref: '#/components/parameters/token' + post: + x-products: + - Storefront + tags: + - Transactions + summary: Finish a transaction KYC verification + operationId: StorefrontPostKycRequestContinue + x-sdk-operation-name: finishKyc + security: + - CustomerJWT: [] + description: >- + Process a KYC interrupted transaction with a specified ID. + + + Use this operation to process the transaction after all documents of the + KYC request with a specified token have been uploaded. + responses: + '200': + description: KYC verification finished. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontTransaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/storefront/transactions/{id}/{token}/bypass': + parameters: + - $ref: '#/components/parameters/resourceId' + - $ref: '#/components/parameters/token' + post: + x-products: + - Storefront + tags: + - Transactions + summary: Skip a transaction KYC verification + operationId: StorefrontPostKycRequestBypass + x-sdk-operation-name: skipKyc + security: + - CustomerJWT: [] + description: >- + Skips an optional KYC request with a specified token, for a transaction + with a specified ID. + + + Use this operation to skip a KYC request that has been marked as + optional in a transaction flow, so that you can further process the + transaction. + responses: + '200': + description: KYC verification skipped. + content: + application/json: + schema: + $ref: '#/components/schemas/StorefrontTransaction' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/storefront/websites/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Storefront + tags: + - Websites + summary: Retrieve a website + operationId: StorefrontGetWebsite + x-sdk-operation-name: get + security: + - CustomerJWT: [] + description: |- + Retrieves a website with a specified ID. + Use this operation to find the website name, logo, or more. + responses: + '200': + description: Website retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Website' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/customers/{id}/edd-score': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Customers + summary: Retrieve a customer's EDD score + operationId: GetCustomerEddScore + x-sdk-operation-name: getCustomerEddScore + description: Retrieves an EDD score for a customer with a specified ID. + responses: + '200': + description: EDD score retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Edd' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + patch: + x-products: + - Core + tags: + - Customers + summary: Update a customer's EDD score + operationId: PatchCustomerEddScore + x-sdk-operation-name: patchCustomerEddScore + description: Updates an EDD score for a customer with a specified ID. + requestBody: + content: + application/json: + schema: + type: object + title: PatchCustomerEddScoreRequest + required: + - score + properties: + score: + type: object + description: Customer's EDD score. + properties: + occupation: + $ref: '#/components/schemas/EddScore' + arrest: + $ref: '#/components/schemas/EddScore' + bankruptcy: + $ref: '#/components/schemas/EddScore' + fraud: + $ref: '#/components/schemas/EddScore' + responses: + '200': + description: EDD score updated. + content: + application/json: + schema: + $ref: '#/components/schemas/Edd' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/customers/{id}/edd-timeline': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Customers timeline + summary: Retrieve EDD timeline messages + operationId: GetCustomerEddTimelineCollection + x-sdk-operation-name: getEddTimelineCollection + description: Retrieves a list of EDD timeline messages. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of EDD timeline messages retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/EddTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Customers timeline + summary: Create an EDD timeline comment + operationId: PostCustomerEddTimeline + x-sdk-operation-name: createEddTimelineComment + description: Creates an EDD timeline comment. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EddTimeline' + description: EDD timeline resource. + required: true + responses: + '201': + description: EDD timeline comment created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/EddTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/customers/{id}/edd-timeline/{messageId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: messageId + in: path + description: ID of the EDD timeline message. + required: true + schema: + type: string + get: + x-products: + - Core + tags: + - Customers timeline + summary: Retrieve an EDD timeline message + operationId: GetCustomerEddTimeline + x-sdk-operation-name: getEddTimelineMessage + description: Retrieves an EDD message with a specified ID. + responses: + '200': + description: EDD timeline message retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/EddTimeline' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + delete: + x-products: + - Core + tags: + - Customers timeline + summary: Delete an EDD timeline message + operationId: DeleteCustomerEddTimeline + x-sdk-operation-name: deleteEddTimelineMessage + description: Deletes an EDD timeline message with a specified ID. + responses: + '204': + description: EDD timeline message deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '/customers/{id}/edd-search-results': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Customers + summary: Retrieve customer EDD search results + operationId: GetCustomerEddSearchResultCollection + x-sdk-operation-name: getAllEddSearchResults + description: Retrieve EDD search results for a customer with a specified ID. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: EDD search results retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/EddSearchResult' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '/customers/{id}/edd-search-results/{searchResultId}': + parameters: + - $ref: '#/components/parameters/resourceId' + - name: searchResultId + in: path + description: ID of the EDD search result. + required: true + schema: + type: string + get: + x-products: + - Full + - Risk Management + - Core + tags: + - Customers + summary: Retrieve a customer's EDD search result + operationId: GetCustomerEddSearchResult + x-sdk-operation-name: getEddSearchResult + description: Retrieves an EDD search result for a customer with a specified ID. + responses: + '200': + description: EDD search result retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/EddSearchResult' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /risk-score-rules: + get: + x-products: + - Core + tags: + - Risk score + summary: Retrieve risk score rules + operationId: GetRiskScoreRules + x-sdk-operation-name: getAll + security: + - SecretApiKey: [] + - JWT: [] + description: Retrieves risk score rules. + responses: + '200': + description: Risk score rules retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/RiskScoreRules' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + put: + x-products: + - Core + tags: + - Risk score + summary: Modify risk score rules + operationId: PutRiskScoreRules + x-sdk-operation-name: updateRiskScoreRules + description: Modifies risk score rules. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RiskScoreRules' + description: Risk score rules. + required: true + responses: + '200': + description: Risk score rules set. + content: + application/json: + schema: + $ref: '#/components/schemas/RiskScoreRules' + '201': + description: Risk score rules set. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/RiskScoreRules' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /risk-score-rules/blocklists: + get: + x-products: + - Core + tags: + - Risk score + summary: Retrieve risk score blocklist rules + operationId: GetRiskScoreBlocklistRules + x-sdk-operation-name: getAllBlocklistRules + security: + - SecretApiKey: [] + - JWT: [] + description: Retrieves risk score blocklist rules. + responses: + '200': + description: Risk score blocklist rules retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/RiskScoreBlocklist' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + put: + x-products: + - Core + tags: + - Risk score + summary: Modify risk score blocklist rules + operationId: PutRiskScoreBlocklistRules + x-sdk-operation-name: updateRiskScoreBlocklistRules + description: Modifies risk score blocklist rules. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RiskScoreBlocklist' + description: Risk score blocklist rules. + required: true + responses: + '200': + description: Risk score blocklist rules set. + content: + application/json: + schema: + $ref: '#/components/schemas/RiskScoreBlocklist' + '201': + description: Risk score blocklist rules set. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/RiskScoreBlocklist' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + /allowlists: + get: + x-products: + - Core + tags: + - Allowlists + summary: Retrieve allowlist collection + operationId: GetAllowlistCollection + x-sdk-operation-name: getAllowlistCollection + security: + - SecretApiKey: [] + - JWT: [] + description: Retrieves allowlist collection. + parameters: + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + responses: + '200': + description: Allowlist collection retrieved. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Allowlist' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Allowlists + summary: Create allowlist record + operationId: PostAllowlist + x-sdk-operation-name: storeAllowlist + security: + - PublishableApiKey: [] + description: Creates allowlist record. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Allowlist' + description: Allowlist record. + required: true + responses: + '201': + description: Allowlist record created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/Allowlist' + '401': + $ref: '#/components/responses/Unauthorized' + '409': + $ref: '#/components/responses/Conflict' + '/allowlists/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Allowlists + summary: Retrieve allowlist record + operationId: GetAllowlist + x-sdk-operation-name: getAllowlist + security: + - SecretApiKey: [] + - JWT: [] + description: Retrieves allowlist record. + responses: + '200': + description: Allowlist record retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/Allowlist' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + delete: + x-products: + - Core + tags: + - Allowlists + summary: Delete allowlist record + operationId: DeleteAllowlist + x-sdk-operation-name: delete + description: Deletes allowlist record with a specified ID. + responses: + '204': + description: Allowlist record deleted. + /tags-rules: + get: + x-products: + - Core + tags: + - Tags + summary: Retrieve tags rules list + operationId: GetTagsRulesCollection + x-sdk-operation-name: getAllTagsRules + description: Retrieves a list of tags rules. + parameters: + - $ref: '#/components/parameters/collectionLimit' + - $ref: '#/components/parameters/collectionOffset' + - $ref: '#/components/parameters/collectionSort' + - $ref: '#/components/parameters/collectionFilter' + - $ref: '#/components/parameters/collectionQuery' + responses: + '200': + description: List of tags rules retrieved. + headers: + Pagination-Total: + $ref: '#/components/headers/Pagination-Total' + Pagination-Limit: + $ref: '#/components/headers/Pagination-Limit' + Pagination-Offset: + $ref: '#/components/headers/Pagination-Offset' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/TagUntagRule' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + x-products: + - Core + tags: + - Tags + summary: Create tags rule + operationId: PostTagRules + x-sdk-operation-name: createTagsRule + description: Creates a tags rule. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TagUntagRule' + description: Tags rule resource. + required: true + responses: + '201': + description: Tags rule created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/TagUntagRule' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + '/tags-rules/{id}': + parameters: + - $ref: '#/components/parameters/resourceId' + get: + x-products: + - Core + tags: + - Tags + summary: Retrieve a tags rule + operationId: GetTagsRule + x-sdk-operation-name: getTagsRule + description: Retrieves a tags rule with a specified ID. + responses: + '200': + description: Tags rule retrieved. + content: + application/json: + schema: + $ref: '#/components/schemas/TagUntagRule' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + put: + x-products: + - Core + tags: + - Tags + summary: Create a tags rule with specified ID + operationId: PutTagsRule + x-sdk-operation-name: updateTagsRule + description: Creates a tags rule with a specified ID. + responses: + '200': + description: Tags rule updated. + content: + application/json: + schema: + $ref: '#/components/schemas/TagUntagRule' + '201': + description: Tags rule created. + headers: + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/TagUntagRule' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/ValidationError' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TagUntagRule' + description: Tags rule resource. + required: true + delete: + x-products: + - Core + tags: + - Tags + summary: Delete tags rule + operationId: DeleteTagsRule + x-sdk-operation-name: deleteTagsRule + description: Deletes a tags rule with a specified ID. + responses: + '204': + description: Tags rule deleted. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' +webhooks: + application-instance-disabled: + post: + summary: Application instance was disabled + operationId: application-instance-disabled + x-products: + - Users + tags: + - Application owners + requestBody: + $ref: '#/components/requestBodies/ApplicationInstanceDisabled' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + application-instance-enabled: + post: + summary: Application instance was enabled + operationId: application-instance-enabled + x-products: + - Users + tags: + - Application owners + requestBody: + $ref: '#/components/requestBodies/ApplicationInstanceEnabled' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + aml-list-possibly-matched: + post: + summary: AML list possibly matched + operationId: aml-list-possibly-matched + x-products: + - Core + tags: + - AML + requestBody: + $ref: '#/components/requestBodies/CustomerWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + balance-transaction-settled: + post: + summary: Balance transaction settled + operationId: balance-transaction-settled + tags: + - Balance transactions + x-products: + - Users + requestBody: + $ref: '#/components/requestBodies/InvoiceRevenueRecognized' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + coupon-application-removed: + post: + summary: Coupon application removed + operationId: coupon-application-removed + x-products: + - Core + tags: + - Coupons + requestBody: + $ref: '#/components/requestBodies/CouponRedemptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + coupon-applied: + post: + summary: Coupon applied + operationId: coupon-applied + x-products: + - Core + tags: + - Coupons + requestBody: + $ref: '#/components/requestBodies/CouponRedemptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + coupon-expiration-modified: + post: + summary: Coupon expiration modified + operationId: coupon-expiration-modified + x-products: + - Core + tags: + - Coupons + requestBody: + $ref: '#/components/requestBodies/CouponWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + coupon-expired: + post: + summary: Coupon expired + operationId: coupon-expired + x-products: + - Core + tags: + - Coupons + requestBody: + $ref: '#/components/requestBodies/CouponWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + coupon-issued: + post: + summary: Coupon issued + operationId: coupon-issued + x-products: + - Core + tags: + - Coupons + requestBody: + $ref: '#/components/requestBodies/CouponWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + coupon-modified: + post: + summary: Coupon modified + operationId: coupon-modified + x-products: + - Core + tags: + - Coupons + requestBody: + $ref: '#/components/requestBodies/CouponWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + coupon-redeemed: + post: + summary: Coupon redeemed + operationId: coupon-redeemed + x-products: + - Core + tags: + - Coupons + requestBody: + $ref: '#/components/requestBodies/CouponRedemptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + coupon-redemption-canceled: + post: + summary: Coupon redemption canceled + operationId: coupon-redemption-canceled + x-products: + - Core + tags: + - Coupons + requestBody: + $ref: '#/components/requestBodies/CouponRedemptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + credit-memo-applied: + post: + summary: Credit memo applied + operationId: credit-memo-applied + x-products: + - Core + tags: + - Credit memos + requestBody: + $ref: '#/components/requestBodies/CreditMemoWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + credit-memo-created: + post: + summary: Credit memo created + operationId: credit-memo-created + x-products: + - Core + tags: + - Credit memos + requestBody: + $ref: '#/components/requestBodies/CreditMemoWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + credit-memo-modified: + post: + summary: Credit memo modified + operationId: credit-memo-modified + x-products: + - Core + tags: + - Credit memos + requestBody: + $ref: '#/components/requestBodies/CreditMemoWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + credit-memo-partially-applied: + post: + summary: Credit memo partially applied + operationId: credit-memo-partially-applied + x-products: + - Core + tags: + - Credit memos + requestBody: + $ref: '#/components/requestBodies/CreditMemoWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + credit-memo-voided: + post: + summary: Credit memo voided + operationId: credit-memo-voided + x-products: + - Core + tags: + - Credit memos + requestBody: + $ref: '#/components/requestBodies/CreditMemoWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + customer-created: + post: + summary: Customer created + operationId: customer-created + x-products: + - Core + tags: + - Customers + requestBody: + $ref: '#/components/requestBodies/CustomerWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + customer-merged: + post: + summary: Customer merged + operationId: customer-merged + x-products: + - Core + tags: + - Customers + requestBody: + $ref: '#/components/requestBodies/MergedCustomer' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + customer-one-time-password-requested: + post: + summary: Customer one-time-password requested + operationId: customer-one-time-password-requested + x-products: + - Core + tags: + - Customers + requestBody: + $ref: '#/components/requestBodies/CustomerWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + customer-redirected-offsite: + post: + summary: Customer redirected offsite + operationId: customer-redirected-offsite + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/CustomerRedirect' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + customer-returned: + post: + summary: Customer returned + operationId: customer-returned + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/CustomerRedirect' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + customer-updated: + post: + summary: Customer updated + operationId: customer-updated + x-products: + - Core + tags: + - Customers + requestBody: + $ref: '#/components/requestBodies/CustomerWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + dispute-created: + post: + summary: Dispute created + operationId: dispute-created + x-products: + - Core + tags: + - Disputes + requestBody: + $ref: '#/components/requestBodies/DisputeWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + dispute-modified: + post: + summary: Dispute modified + operationId: dispute-modified + x-products: + - Core + tags: + - Disputes + requestBody: + $ref: '#/components/requestBodies/DisputeWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + experian-check-performed: + post: + summary: Experian check performed + operationId: experian-check-performed + x-products: + - Core + tags: + - Customers + requestBody: + $ref: '#/components/requestBodies/ExperianCheckPerformed' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + hard-usage-limit-reached: + post: + summary: Soft usage limit reached + operationId: soft-usage-limit-reached + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/UsageLimitWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-tax-calculation-failed: + post: + summary: Invoice tax calculation failed + operationId: invoice-tax-calculation-failed + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceTaxCalculationFailed' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + gateway-account-downtime-ended: + post: + summary: Gateway account downtime ended + operationId: gateway-account-downtime-ended + x-products: + - Users + tags: + - Gateway accounts + requestBody: + $ref: '#/components/requestBodies/GatewayAccountWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + gateway-account-downtime-started: + post: + summary: Gateway account downtime started + operationId: gateway-account-downtime-started + x-products: + - Users + tags: + - Gateway accounts + requestBody: + $ref: '#/components/requestBodies/GatewayAccountWebhook' + responses: + 2XX: + description: Returns any 2xx status to indicate that data is received. + gateway-account-limit-reached: + post: + summary: Gateway account limit reached + operationId: gateway-account-limit-reached + x-products: + - Users + tags: + - Gateway accounts + requestBody: + $ref: '#/components/requestBodies/GatewayAccountAndGatewayAccountLimit' + responses: + 2XX: + description: Returns any 2xx status to indicate that data is received. + gateway-account-requested: + post: + summary: Gateway account requested + operationId: gateway-account-requested + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/TransactionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-abandoned: + post: + summary: Invoice abandoned + operationId: invoice-abandoned + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-created: + post: + summary: Invoice created + operationId: invoice-created + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-issued: + post: + summary: Invoice issued + operationId: invoice-issued + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-modified: + post: + summary: Invoice modified + operationId: invoice-modified + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-paid: + post: + summary: Invoice paid + operationId: invoice-paid + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-partially-paid: + post: + summary: Invoice partially paid + operationId: invoice-partially-paid + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-refunded: + post: + summary: Invoice refunded + operationId: invoice-refunded + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-partially-refunded: + post: + summary: Invoice partially refunded + operationId: invoice-partially-refunded + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-past-due: + post: + summary: Invoice past due + operationId: invoice-past-due + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-past-due-reminder: + post: + summary: Invoice past due reminder + operationId: invoice-past-due-reminder + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-reissued: + post: + summary: Invoice reissued + operationId: invoice-reissued + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + invoice-voided: + post: + summary: Invoice voided + operationId: invoice-voided + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + kyc-document-accepted: + post: + summary: KYC document accepted + operationId: kyc-document-accepted + x-products: + - Core + tags: + - KYC documents + requestBody: + $ref: '#/components/requestBodies/KycDocumentWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + kyc-document-modified: + post: + summary: KYC document modified + operationId: kyc-document-modified + x-products: + - Core + tags: + - KYC documents + requestBody: + $ref: '#/components/requestBodies/KycDocumentWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + kyc-document-rejected: + post: + summary: KYC document rejected + operationId: kyc-document-rejected + x-products: + - Core + tags: + - KYC documents + requestBody: + $ref: '#/components/requestBodies/KycDocumentWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + kyc-document-reviewed: + post: + summary: KYC document reviewed + operationId: kyc-document-reviewed + x-products: + - Core + tags: + - KYC documents + requestBody: + $ref: '#/components/requestBodies/KycDocumentWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + kyc-document-archived: + post: + summary: KYC document archived + operationId: kyc-document-archived + x-products: + - Core + tags: + - KYC documents + requestBody: + $ref: '#/components/requestBodies/KycDocumentWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + kyc-request-attempted: + post: + summary: KYC request attempted + operationId: kyc-request-attempted + x-products: + - Core + tags: + - KYC documents + requestBody: + $ref: '#/components/requestBodies/KycRequestWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + kyc-request-failed: + post: + summary: KYC request failed + operationId: kyc-request-failed + x-products: + - Core + tags: + - KYC documents + requestBody: + $ref: '#/components/requestBodies/KycRequestWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + kyc-request-fulfilled: + post: + summary: KYC request fulfilled + operationId: kyc-request-fulfilled + x-products: + - Core + tags: + - KYC documents + requestBody: + $ref: '#/components/requestBodies/KycRequestWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + kyc-request-partially-fulfilled: + post: + summary: KYC request partially fulfilled + operationId: kyc-request-partially-fulfilled + x-products: + - Core + tags: + - KYC documents + requestBody: + $ref: '#/components/requestBodies/KycRequestWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + nsf-response-received: + post: + summary: NSF response received + operationId: nsf-response-received + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/TransactionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + offsite-payment-completed: + post: + summary: Offsite payment completed + operationId: offsite-payment-completed + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/TransactionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + order-abandoned: + post: + summary: Order abandoned + operationId: order-abandoned + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + order-completed: + post: + summary: Order completed + operationId: order-completed + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + payment-card-created: + post: + summary: Payment card created + operationId: payment-card-created + x-products: + - Core + tags: + - Payment instruments + requestBody: + $ref: '#/components/requestBodies/PaymentCardWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + payment-card-expiration-reminder: + post: + summary: Payment card expiration reminder + operationId: payment-card-expiration-reminder + x-products: + - Core + tags: + - Payment instruments + requestBody: + $ref: '#/components/requestBodies/PaymentCardWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + payment-card-expired: + post: + summary: Payment card expired + operationId: payment-card-expired + x-products: + - Core + tags: + - Payment instruments + requestBody: + $ref: '#/components/requestBodies/PaymentCardWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + payment-instrument-modified: + post: + summary: Payment instrument modified + operationId: payment-instrument-modified + x-products: + - Core + tags: + - Payment instruments + requestBody: + $ref: '#/components/requestBodies/PaymentInstrumentWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + payout-request-created: + post: + summary: Payout request created + operationId: payout-request-created + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/PayoutRequestWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + payout-request-modified: + post: + summary: Payout request modified + operationId: payout-request-modified + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/PayoutRequestWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + renewal-invoice-issued: + post: + summary: Renewal invoice issued + operationId: renewal-invoice-issued + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/RenewalInvoiceIssued' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + renewal-invoice-payment-canceled: + post: + summary: Renewal invoice payment canceled + operationId: renewal-invoice-payment-canceled + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceAndTransaction' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + renewal-invoice-payment-declined: + post: + summary: Renewal invoice payment declined + operationId: renewal-invoice-payment-declined + x-products: + - Core + tags: + - Invoices + requestBody: + $ref: '#/components/requestBodies/InvoiceAndTransaction' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + risk-score-changed: + post: + summary: Risk score changed + operationId: risk-score-changed + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/TransactionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + soft-usage-limit-reached: + post: + summary: Hard usage limit reached + operationId: hard-usage-limit-reached + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/UsageLimitWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-activated: + post: + summary: Subscription order activated + operationId: subscription-activated + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-canceled: + post: + summary: Subscription order canceled + operationId: subscription-canceled + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-churned: + post: + summary: Subscription order churned + operationId: subscription-churned + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-downgraded: + post: + summary: Subscription order downgraded + operationId: subscription-downgraded + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-modified: + post: + summary: Order modified + operationId: subscription-modified + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-reactivated: + post: + summary: Subscription order reactivated + operationId: subscription-reactivated + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-renewal-reminder: + post: + summary: Subscription renewal reminder + operationId: subscription-renewal-reminder + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-renewed: + post: + summary: Subscription order renewed + operationId: subscription-renewed + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-trial-converted: + post: + summary: Subscription order trial converted + operationId: subscription-trial-converted + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-trial-end-reminder: + post: + summary: Subscription order trial end reminder + operationId: subscription-trial-end-reminder + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-trial-ended: + post: + summary: Subscription order trial ended + operationId: subscription-trial-ended + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-trial-end-changed: + post: + summary: Subscription order trial end changed + operationId: subscription-trial-end-changed + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-upgraded: + post: + summary: Subscription order upgraded + operationId: subscription-upgraded + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-paused: + post: + summary: Subscription order paused + operationId: subscription-paused + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionAndSubscriptionPause' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-pause-created: + post: + summary: Subscription order pause created + operationId: subscription-pause-created + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionAndSubscriptionPause' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-pause-modified: + post: + summary: Subscription order pause modified + operationId: subscription-pause-modified + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionAndSubscriptionPause' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-pause-revoked: + post: + summary: Subscription order pause revoked + operationId: subscription-pause-revoked + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionAndSubscriptionPause' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + subscription-resumed: + post: + summary: Subscription order resumed + operationId: subscription-resumed + x-products: + - Core + tags: + - Orders + requestBody: + $ref: '#/components/requestBodies/SubscriptionAndSubscriptionPause' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + transaction-amount-discrepancy-found: + post: + summary: Transaction amount discrepancy found + operationId: transaction-amount-discrepancy-found + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/TransactionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + transaction-declined: + post: + summary: Transaction declined + operationId: transaction-declined + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/TransactionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + transaction-discrepancy-found: + post: + summary: Transaction discrepancy found + operationId: transaction-discrepancy-found + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/TransactionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + transaction-process-requested: + post: + summary: Transaction process requested + operationId: transaction-process-requested + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/TransactionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + transaction-processed: + post: + summary: Transaction processed + operationId: transaction-processed + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/TransactionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + transaction-timeout-resolved: + post: + summary: Transaction timeout resolved + operationId: transaction-timeout-resolved + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/TransactionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + waiting-gateway-transaction-completed: + post: + summary: Waiting gateway transaction completed + operationId: waiting-gateway-transaction-completed + x-products: + - Core + tags: + - Transactions + requestBody: + $ref: '#/components/requestBodies/TransactionWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. + data-export-completed: + post: + summary: Data export completed + operationId: data-export-completed + x-products: + - Reports + tags: + - Data exports + requestBody: + $ref: '#/components/requestBodies/DataExportWebhook' + responses: + 2xx: + description: Returns any 2xx status to indicate that data is received. diff --git a/__tests__/respect/rebilly/rebilly.test.ts b/__tests__/respect/rebilly/rebilly.test.ts new file mode 100644 index 0000000000..dfdd0bead8 --- /dev/null +++ b/__tests__/respect/rebilly/rebilly.test.ts @@ -0,0 +1,11 @@ +import { getParams, getCommandOutput, getFixturePath } from '../utils'; +import { join } from 'path'; + +test('rebilly test case', () => { + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'rebilly.yaml'); + const args = getParams(indexEntryPoint, ['respect', fixturesPath]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); +}); diff --git a/__tests__/respect/rebilly/rebilly.yaml b/__tests__/respect/rebilly/rebilly.yaml new file mode 100644 index 0000000000..5508d66aaf --- /dev/null +++ b/__tests__/respect/rebilly/rebilly.yaml @@ -0,0 +1,124 @@ +arazzo: 1.0.1 +info: + title: Rebilly API + version: latest +sourceDescriptions: + - name: rebilly-description + type: openapi + url: rebilly-description.yaml + x-serverUrl: https://api-sandbox.rebilly.com/organizations/redocly + +workflows: + - workflowId: crud + inputs: + type: object + properties: + env: + type: object + properties: + SANDBOX_REBILLY_TOKEN: + type: string + parameters: + - in: header + name: reb-apikey + value: $inputs.env.SANDBOX_REBILLY_TOKEN + steps: + - stepId: create-member + x-operation: + url: https://api-sandbox.rebilly.com/organizations/redocly/customers + method: post + requestBody: + payload: + primaryAddress: + firstName: First + lastName: Last + defaultPaymentInstrument: + method: check + successCriteria: + - condition: $statusCode == 201 + outputs: + id: $response.body#/id + - stepId: read-member + x-operation: + url: https://api-sandbox.rebilly.com/organizations/redocly/customers/{id} + method: get + parameters: + - in: path + name: id + value: $steps.create-member.outputs.id + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/firstName == 'First' + - condition: $response.body#/lastName == 'Last' + + - stepId: update-member + x-operation: + url: https://api-sandbox.rebilly.com/organizations/redocly/customers/{id} + method: put + parameters: + - in: path + name: id + value: $steps.create-member.outputs.id + requestBody: + payload: + primaryAddress: + firstName: UpdatedName + lastName: Last + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/firstName == 'UpdatedName' + - condition: $response.body#/lastName == 'Last' + - condition: $response.body#/id == $steps.create-member.outputs.id + - stepId: delete-member + x-operation: + method: delete + url: https://api-sandbox.rebilly.com/organizations/redocly/customers/{id} + parameters: + - in: path + name: id + value: $steps.create-member.outputs.id + - in: query + name: targetCustomerId + value: cus_01GYWHGJAJYC3SKM8BKWKZA0SZ # Technical customer ID + successCriteria: + - condition: $statusCode == 204 + - stepId: read-non-existing-member + x-operation: + method: get + url: https://api-sandbox.rebilly.com/organizations/redocly/customers/{id} + parameters: + - in: path + name: id + value: $steps.create-member.outputs.id + successCriteria: + - condition: $statusCode == 404 + + # - workflowId: get-all-users + # steps: + # - stepId: getUsers + # x-operation: + # url: https://api-sandbox.rebilly.com/organizations/redocly/customers + # method: get + # parameters: + # - in: query + # name: limit + # value: 1 + # successCriteria: + # - condition: $statusCode == 200 + + - workflowId: inherited + inputs: + type: object + properties: + env: + type: object + properties: + SANDBOX_REBILLY_TOKEN: + type: string + parameters: + - in: header + name: reb-apikey + value: $inputs.env.SANDBOX_REBILLY_TOKEN + steps: + - stepId: add-user + operationId: PostCustomer diff --git a/__tests__/respect/reusable-components/__snapshots__/reusable-components.test.ts.snap b/__tests__/respect/reusable-components/__snapshots__/reusable-components.test.ts.snap new file mode 100644 index 0000000000..bb93e5a868 --- /dev/null +++ b/__tests__/respect/reusable-components/__snapshots__/reusable-components.test.ts.snap @@ -0,0 +1,509 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should use inputs from CLI and env to mapp with resolved refs 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow reusable-components.yaml / get-museum-hours + + ✓ GET /museum-hours - step get-museum-hours-step-success-action + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/museum-hours +    Request Headers: +      accept: application/json, application/problem+json +      pagesize: 256 +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "date": "2023-09-11", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-12", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-13", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-14", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-15", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       }, +       { +       "date": "2023-09-18", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-19", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-20", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-21", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-22", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       } +      ] + +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✗ GET /museum-hours - step get-museum-hours-step-failure-action-goto + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/museum-hours +    Request Headers: +      accept: application/json, application/problem+json +      pagesize: 256 +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "date": "2023-09-11", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-12", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-13", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-14", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-15", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       }, +       { +       "date": "2023-09-18", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-19", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-20", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-21", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-22", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       } +      ] + +    ✗ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + Running required workflows for get-museum-hours  + + ✓ GET /museum-hours - step final-workflow-step + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/museum-hours +    Request Headers: +      accept: application/json, application/problem+json +      pagesize: 256 +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "date": "2023-09-11", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-12", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-13", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-14", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-15", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       }, +       { +       "date": "2023-09-18", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-19", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-20", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-21", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-22", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       } +      ] + +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow reusable-components.yaml / events-crud + + ✓ GET /special-events - step list-events + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      accept: application/json, application/problem+json +      pagesize: 256 +      authorization: Basic Og== +      page: 1 + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "eventId": "f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97", +       "name": "Sasquatch Ballet", +       "location": "Seattle... probably", +       "eventDescription": "They're big, they're hairy, but they're also graceful. Come learn how the biggest feet can have the lightest touch.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 40 +       }, +       { +       "eventId": "2f14374a-9c65-4ee5-94b7-fba66d893483", +       "name": "Solar Telescope Demonstration", +       "location": "Far from the sun.", +       "eventDescription": "Look at the sun without going blind!", +       "dates": [ +       "2023-09-07", +       "2023-09-14" +       ], +       "price": 50 +       }, +       { +       "eventId": "6aaa61ba-b2aa-4868-b803-603dbbf7bfdb", +       "name": "Cook like a Caveman", +       "location": "Fire Pit on East side", +       "eventDescription": "Learn to cook on an open flame.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       }, +       { +       "eventId": "602b75e1-5696-4ab8-8c7a-f9e13580f910", +       "name": "Underwater Basket Weaving", +       "location": "Rec Center Pool next door.", +       "eventDescription": "Learn to weave baskets underwater.", +       "dates": [ +       "2023-09-12", +       "2023-09-15" +       ], +       "price": 15 +       }, +       { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Room Sea-12", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 30 +       }, +       { +       "eventId": "6744a0da-4121-49cd-8479-f8cc20526495", +       "name": "Time Traveler Tea Party", +       "location": "Temporal Tearoom", +       "eventDescription": "Sip tea with important historical figures.", +       "dates": [ +       "2023-11-18", +       "2023-11-25", +       "2023-12-02" +       ], +       "price": 60 +       }, +       { +       "eventId": "3be6453c-03eb-4357-ae5a-984a0e574a54", +       "name": "Pirate Coding Workshop", +       "location": "Computer Room", +       "eventDescription": "Captain Blackbeard shares his love of the C...language. And possibly Arrrrr (R lang).", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "9d90d29a-2af5-4206-97d9-9ea9ceadcb78", +       "name": "Llama Street Art Through the Ages", +       "location": "Auditorium", +       "eventDescription": "Llama street art?! Alpaca my bags -- let's go!", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "a3c7b2c4-b5fb-4ef7-9322-00a919864957", +       "name": "The Great Parrot Debate", +       "location": "Outdoor Amphitheatre", +       "eventDescription": "See leading parrot minds discuss important geopolitical issues.", +       "dates": [ +       "2023-11-03", +       "2023-11-10" +       ], +       "price": 35 +       }, +       { +       "eventId": "b92d46b7-4c5d-422b-87a5-287767e26f29", +       "name": "Eat a Bunch of Corn", +       "location": "Cafeteria", +       "eventDescription": "We accidentally bought too much corn. Please come eat it.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       } +      ] + +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✓ POST /special-events - step create-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      content-type: application/json +      accept: application/json, application/problem+json +      pagesize: 256 +      authorization: Basic Og== +      page: 42 +    Request Body: +      { +       "reusable": "\\"123\\"", +       "store_id": "42", +       "my_pet_tags": [ +       "one", +       "two" +       ], +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + + +    Response status code: 201 +    Response time: ms +    Response Body: +      { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow reusable-components.yaml / final-workflow + + ✓ GET /museum-hours - step final-workflow-step + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/museum-hours +    Request Headers: +      accept: application/json, application/problem+json +      pagesize: 256 +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "date": "2023-09-11", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-12", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-13", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-14", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-15", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       }, +       { +       "date": "2023-09-18", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-19", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-20", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-21", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-22", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       } +      ] + +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +  Failed tests info: + +  Workflow name: get-museum-hours + +    stepId - get-museum-hours-step-failure-action-goto +    ✗ success criteria check +      Checking simple criteria: {"condition":"$statusCode == 201"} +       +  Summary for reusable-components.yaml +   +  Workflows: 2 passed, 1 failed, 3 total +  Steps: 5 passed, 1 failed, 6 total +  Checks: 18 passed, 1 failed, 19 total +  Time: ms + + +┌──────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├──────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ x reusable-components.yaml │ 3 │ 2 │ 1 │ - │ - │ +└──────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + + + Tests exited with error +" +`; \ No newline at end of file diff --git a/__tests__/respect/reusable-components/museum-api.yaml b/__tests__/respect/reusable-components/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/reusable-components/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/reusable-components/museum-tickets.yaml b/__tests__/respect/reusable-components/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/reusable-components/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/reusable-components/reusable-components.test.ts b/__tests__/respect/reusable-components/reusable-components.test.ts new file mode 100644 index 0000000000..7ce9bdae00 --- /dev/null +++ b/__tests__/respect/reusable-components/reusable-components.test.ts @@ -0,0 +1,23 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +test('should use inputs from CLI and env to mapp with resolved refs', () => { + process.env.AUTH_TOKEN = 'Basic Og=='; + + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'reusable-components.yaml'); + const args = getParams(indexEntryPoint, [ + 'respect', + fixturesPath, + '--verbose', + '--input', + '{"store_id":"42","my_pet_tags":["one","two"]}', + '--input', + 'reusable-test="123"', + ]); + + const result = getCommandOutput(args); + + expect(result).toMatchSnapshot(); + delete process.env.AUTH_TOKEN; +}); diff --git a/__tests__/respect/reusable-components/reusable-components.yaml b/__tests__/respect/reusable-components/reusable-components.yaml new file mode 100644 index 0000000000..ec9318c40c --- /dev/null +++ b/__tests__/respect/reusable-components/reusable-components.yaml @@ -0,0 +1,151 @@ +arazzo: 1.0.1 +info: + title: Test reusable $components + description: Testing functionality of reusable $components + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + inputs: + $ref: '#/components/inputs/reusable-test' + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - reference: $components.parameters.pageSize + value: 256 + - in: header + name: Authorization + value: Basic Og== + outputs: + reusable-test: $inputs.reusable-test + steps: + - stepId: get-museum-hours-step-success-action + operationId: $sourceDescriptions.museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body + - stepId: get-museum-hours-step-failure-action-goto + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: $sourceDescriptions.museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 201 + outputs: + schedule: $response.body + onFailure: + - reference: $components.failureActions.gotoFailureAction + - stepId: get-museum-hours-step-failure-action-retry + operationId: $sourceDescriptions.museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 201 + outputs: + schedule: $response.body + onFailure: + - reference: $components.failureActions.retryFailureAction + - workflowId: events-crud + inputs: + $ref: '#/components/inputs/buy_available_pet_input' + parameters: + - reference: $components.parameters.pageSize + value: 256 + - in: header + name: Authorization + value: Basic Og== + - reference: $components.parameters.page + value: 42 + steps: + - stepId: list-events + parameters: + - reference: $components.parameters.page + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + - stepId: create-event + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + requestBody: + payload: + reusable: $workflows.get-museum-hours.outputs.reusable-test + store_id: $inputs.store_id + my_pet_tags: $inputs.my_pet_tags + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 201 + - workflowId: final-workflow + inputs: + $ref: '#/components/inputs/reusable-test' + parameters: + - reference: $components.parameters.pageSize + value: 256 + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: final-workflow-step + operationId: $sourceDescriptions.museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + +components: + inputs: + reusable-test: + type: string + description: This is a reusable input component + buy_available_pet_input: + type: object + properties: + my_pet_tags: + type: array + items: + type: string + description: Desired tags to use when searching for a pet, in CSV format (e.g. "puppy, dalmatian") + store_id: + $ref: '#/components/inputs/store_id' + store_id: + type: string + description: Indicates the domain name of the store where the customer is browsing or buying pets, e.g. "pets.example.com" or "pets.example.co.uk". + parameters: + page: + name: page + in: header + value: 1 + pageSize: + name: pageSize + in: header + value: 100 + failureActions: + gotoFailureAction: + name: get-museum-hours-failure-action + type: goto + workflowId: final-workflow + criteria: + - condition: $statusCode == 200 + retryFailureAction: + name: get-museum-hours-failure-action + type: retry + retryAfter: 3000 + retryLimit: 1 + workflowId: final-workflow + criteria: + - condition: $statusCode == 200 + successActions: + gotoSuccessAction: + name: get-museum-hours-success-action + type: goto + workflowId: final-workflow + criteria: + - condition: $statusCode == 200 diff --git a/__tests__/respect/server-override-with-console-parameters/__snapshots__/server-override-with-console-parameters.test.ts.snap b/__tests__/respect/server-override-with-console-parameters/__snapshots__/server-override-with-console-parameters.test.ts.snap new file mode 100644 index 0000000000..b26736246c --- /dev/null +++ b/__tests__/respect/server-override-with-console-parameters/__snapshots__/server-override-with-console-parameters.test.ts.snap @@ -0,0 +1,87 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should use server override from CLI and env 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow server-override-with-console-parameters.yaml / get-museum-hours + + ✗ GET /museum-hours - step get-museum-hours + +    Request URL: https://museum-api-bad-endpoint.com/museum-api-bad-endpoint/museum-hours +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + +    ✗ failed network request + + Running step buy-ticket workflow $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + + ✗ POST /tickets - step buy-tickets +    ✗ failed network request +──────────────────────────────────────────────────────────────────────────────── + + Running workflow server-override-with-console-parameters.yaml / events-crud + + ✗ GET /special-events - step list-events + +    Request URL: https://museum-api-bad-endpoint.com/museum-api-bad-endpoint/special-events +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + +    ✗ failed network request + + +  Failed tests info: + +  Workflow name: get-museum-hours + +    stepId - get-museum-hours +    ✗ failed network request +      fetch failed +       +    stepId - buy-ticket +    ✗ unexpected error +    Reason: Failed to resolve outputs in workflow "get-museum-tickets": Error in resolving runtime expression '$steps.buy-tickets.outputs.ticketId'. +    This could be because the expression references a value from a previous failed step, or is trying to reference a variable that hasn't been set. +  Workflow name: events-crud + +    stepId - list-events +    ✗ failed network request +      fetch failed +       +    stepId - create-event +    ✗ unexpected error +    Reason: Error in resolving runtime expression '$steps.list-events.outputs.events.0.dates.0'. +    This could be because the expression references a value from a previous failed step, or is trying to reference a variable that hasn't been set. +    stepId - get-event-by-id +    ✗ unexpected error +    Reason: Error in resolving runtime expression '$steps.create-event.outputs.createdEventId'. +    This could be because the expression references a value from a previous failed step, or is trying to reference a variable that hasn't been set. +    stepId - update-event +    ✗ unexpected error +    Reason: Error in resolving runtime expression '$steps.create-event.outputs.createdEventId'. +    This could be because the expression references a value from a previous failed step, or is trying to reference a variable that hasn't been set. +    stepId - delete-event +    ✗ unexpected error +    Reason: Error in resolving runtime expression '$steps.update-event.outputs.updatedEventId'. +    This could be because the expression references a value from a previous failed step, or is trying to reference a variable that hasn't been set. +  Summary for server-override-with-console-parameters.yaml +   +  Workflows: 2 failed, 2 total +  Steps: 7 failed, 7 total +  Checks: 7 failed, 7 total +  Time: ms + + +┌──────────────────────────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├──────────────────────────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ x server-override-with-console-parameters.yaml │ 2 │ 0 │ 2 │ - │ - │ +└──────────────────────────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + + + Tests exited with error +" +`; diff --git a/__tests__/respect/server-override-with-console-parameters/museum-api.yaml b/__tests__/respect/server-override-with-console-parameters/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/server-override-with-console-parameters/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/server-override-with-console-parameters/museum-tickets.yaml b/__tests__/respect/server-override-with-console-parameters/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/server-override-with-console-parameters/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/server-override-with-console-parameters/server-override-with-console-parameters.test.ts b/__tests__/respect/server-override-with-console-parameters/server-override-with-console-parameters.test.ts new file mode 100644 index 0000000000..8bfd510331 --- /dev/null +++ b/__tests__/respect/server-override-with-console-parameters/server-override-with-console-parameters.test.ts @@ -0,0 +1,19 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +// Snapshot is intentionally should show failed request to museum-api-bad-endpoint +test('should use server override from CLI and env', () => { + process.env.AUTH_TOKEN = 'Basic Og=='; + process.env.REDOCLY_CLI_RESPECT_SERVER = + 'museum-api=https://museum-api-bad-endpoint.com/museum-api-bad-endpoint,tickets-from-museum-api=https://redocly.com/_mock/docs/openapi/museum-api'; + + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'server-override-with-console-parameters.yaml'); + const args = getParams(indexEntryPoint, ['respect', fixturesPath, '--verbose']); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); + + delete process.env.AUTH_TOKEN; + delete process.env.REDOCLY_CLI_RESPECT_SERVER; +}); diff --git a/__tests__/respect/server-override-with-console-parameters/server-override-with-console-parameters.yaml b/__tests__/respect/server-override-with-console-parameters/server-override-with-console-parameters.yaml new file mode 100644 index 0000000000..ab2ce44844 --- /dev/null +++ b/__tests__/respect/server-override-with-console-parameters/server-override-with-console-parameters.yaml @@ -0,0 +1,122 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API + description: >- + An imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body + - stepId: buy-ticket + description: >- + Buy a ticket for the museum by calling an external workflow from another Arazzo file. + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + outputs: + ticketId: $outputs.ticketId + - workflowId: events-crud + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: list-events + description: >- + Request the list of events. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + - stepId: create-event + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + requestBody: + payload: + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - $steps.list-events.outputs.events.0.dates.0 + - $steps.list-events.outputs.events.0.dates.1 + price: 0 + successCriteria: + - condition: $statusCode == 201 + - context: $response.body + condition: $.name == 'Mermaid Treasure Identification and Analysis' + type: jsonpath + outputs: + createdEventId: $response.body#/eventId + name: $response.body#/name + + - stepId: get-event-by-id + description: >- + Get the details of the event that was created in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/get' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + successCriteria: + - context: $statusCode + condition: '^200$' + type: regex + - stepId: update-event + description: >- + Update the created event with new details. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/patch' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + requestBody: + payload: + name: 'Orca Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 200 + - context: $response.body + condition: $.name == 'Orca Identification and Analysis' + type: + type: jsonpath + version: draft-goessner-dispatch-jsonpath-00 + outputs: + updatedEventId: $response.body#/eventId + - stepId: delete-event + description: >- + Delete the event that was updated in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/delete' + parameters: + - name: eventId + in: path + value: $steps.update-event.outputs.updatedEventId + successCriteria: + - condition: $statusCode == 204 diff --git a/__tests__/respect/severity-error-level/__snapshots__/severity-error-level.test.ts.snap b/__tests__/respect/severity-error-level/__snapshots__/severity-error-level.test.ts.snap new file mode 100644 index 0000000000..1dcc8191a7 --- /dev/null +++ b/__tests__/respect/severity-error-level/__snapshots__/severity-error-level.test.ts.snap @@ -0,0 +1,388 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should use error severity level 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow severity-level.yaml / get-museum-hours + + ✗ GET /museum-hours - step get-museum-hours + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/museum-hours +    Request Headers: +      accept: html/text, application/problem+json +      authorization: Basic Og== +      content-type: application/json + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "date": "2023-09-11", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-12", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-13", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-14", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-15", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       }, +       { +       "date": "2023-09-18", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-19", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-20", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-21", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-22", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       } +      ] + +    ✓ success criteria check +    ✗ status code check + + Running step buy-ticket workflow $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + + ✓ POST /tickets - step buy-tickets +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow severity-level.yaml / events-crud + + ✓ GET /special-events - step list-events + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "eventId": "f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97", +       "name": "Sasquatch Ballet", +       "location": "Seattle... probably", +       "eventDescription": "They're big, they're hairy, but they're also graceful. Come learn how the biggest feet can have the lightest touch.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 40 +       }, +       { +       "eventId": "2f14374a-9c65-4ee5-94b7-fba66d893483", +       "name": "Solar Telescope Demonstration", +       "location": "Far from the sun.", +       "eventDescription": "Look at the sun without going blind!", +       "dates": [ +       "2023-09-07", +       "2023-09-14" +       ], +       "price": 50 +       }, +       { +       "eventId": "6aaa61ba-b2aa-4868-b803-603dbbf7bfdb", +       "name": "Cook like a Caveman", +       "location": "Fire Pit on East side", +       "eventDescription": "Learn to cook on an open flame.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       }, +       { +       "eventId": "602b75e1-5696-4ab8-8c7a-f9e13580f910", +       "name": "Underwater Basket Weaving", +       "location": "Rec Center Pool next door.", +       "eventDescription": "Learn to weave baskets underwater.", +       "dates": [ +       "2023-09-12", +       "2023-09-15" +       ], +       "price": 15 +       }, +       { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Room Sea-12", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 30 +       }, +       { +       "eventId": "6744a0da-4121-49cd-8479-f8cc20526495", +       "name": "Time Traveler Tea Party", +       "location": "Temporal Tearoom", +       "eventDescription": "Sip tea with important historical figures.", +       "dates": [ +       "2023-11-18", +       "2023-11-25", +       "2023-12-02" +       ], +       "price": 60 +       }, +       { +       "eventId": "3be6453c-03eb-4357-ae5a-984a0e574a54", +       "name": "Pirate Coding Workshop", +       "location": "Computer Room", +       "eventDescription": "Captain Blackbeard shares his love of the C...language. And possibly Arrrrr (R lang).", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "9d90d29a-2af5-4206-97d9-9ea9ceadcb78", +       "name": "Llama Street Art Through the Ages", +       "location": "Auditorium", +       "eventDescription": "Llama street art?! Alpaca my bags -- let's go!", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "a3c7b2c4-b5fb-4ef7-9322-00a919864957", +       "name": "The Great Parrot Debate", +       "location": "Outdoor Amphitheatre", +       "eventDescription": "See leading parrot minds discuss important geopolitical issues.", +       "dates": [ +       "2023-11-03", +       "2023-11-10" +       ], +       "price": 35 +       }, +       { +       "eventId": "b92d46b7-4c5d-422b-87a5-287767e26f29", +       "name": "Eat a Bunch of Corn", +       "location": "Cafeteria", +       "eventDescription": "We accidentally bought too much corn. Please come eat it.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       } +      ] + +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✓ POST /special-events - step create-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      content-type: application/json +      accept: application/json, application/problem+json +      authorization: Basic Og== +    Request Body: +      { +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 0 +      } + + +    Response status code: 201 +    Response time: ms +    Response Body: +      { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 0 +      } + +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✓ GET /special-events/{eventId} - step get-event-by-id + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events/dad4bce8-f5cb-4078-a211-995864315e39 +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      { +       "eventId": "6744a0da-4121-49cd-8479-f8cc20526495", +       "name": "Time Traveler Tea Party", +       "location": "Temporal Tearoom", +       "eventDescription": "Sip tea with important historical figures.", +       "dates": [ +       "2023-11-18", +       "2023-11-25", +       "2023-12-02" +       ], +       "price": 60 +      } + +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✗ PATCH /special-events/{eventId} - step update-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events/dad4bce8-f5cb-4078-a211-995864315e39 +    Request Headers: +      content-type: application/json +      accept: json, application/problem+json +      authorization: Basic Og== +    Request Body: +      { +       "name": "Orca Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + + +    Response status code: 200 +    Response time: ms +    Response Body: +      { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Orca Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✗ content-type check + + ✗ DELETE /special-events/{eventId} - step delete-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events/dad4bce8-f5cb-4078-a211-995864315e39 +    Request Headers: +      accept: application/problem+json +      authorization: Basic Og== + + +    Response status code: 204 +    Response time: ms +    Response Body: +      {} + +    ✗ success criteria check +    ✗ status code check + + +  Failed tests info: + +  Workflow name: get-museum-hours + +    stepId - get-museum-hours +    ✗ status code check +       +       +  Workflow name: events-crud + +    stepId - update-event +    ✗ content-type check +      Content type application/json for 200 response is not described in the schema. +      Expected content types: json. +       +    stepId - delete-event +    ✗ success criteria check +      Checking simple criteria: {"condition":"$statusCode == 200"} +       +    ✗ status code check +       +       +  Summary for severity-level.yaml +   +  Workflows: 2 failed, 2 total +  Steps: 4 passed, 3 failed, 7 total +  Checks: 20 passed, 4 failed, 24 total +  Time: ms + + +┌─────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├─────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ x severity-level.yaml │ 2 │ 0 │ 2 │ - │ - │ +└─────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + + + Tests exited with error +" +`; diff --git a/__tests__/respect/severity-error-level/museum-api-with-errors.yaml b/__tests__/respect/severity-error-level/museum-api-with-errors.yaml new file mode 100644 index 0000000000..98fcaf666a --- /dev/null +++ b/__tests__/respect/severity-error-level/museum-api-with-errors.yaml @@ -0,0 +1,755 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '201': + description: Success. + content: + html/text: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '205': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + sample: + type: string + example: 'sample' + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/severity-error-level/museum-api.yaml b/__tests__/respect/severity-error-level/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/severity-error-level/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/severity-error-level/museum-tickets.yaml b/__tests__/respect/severity-error-level/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/severity-error-level/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/severity-error-level/severity-error-level.test.ts b/__tests__/respect/severity-error-level/severity-error-level.test.ts new file mode 100644 index 0000000000..0eaabf9c2d --- /dev/null +++ b/__tests__/respect/severity-error-level/severity-error-level.test.ts @@ -0,0 +1,23 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +test('should use error severity level', () => { + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'severity-level.yaml'); + const args = getParams(indexEntryPoint, [ + 'respect', + fixturesPath, + '--verbose', + '--severity', + 'STATUS_CODE_CHECK=error', + '--severity', + 'SCHEMA_CHECK=error', + '--severity', + 'SUCCESS_CRITERIA_CHECK=error', + '--severity', + 'CONTENT_TYPE_CHECK=error', + ]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); +}); diff --git a/__tests__/respect/severity-error-level/severity-level.yaml b/__tests__/respect/severity-error-level/severity-level.yaml new file mode 100644 index 0000000000..65aae58dc9 --- /dev/null +++ b/__tests__/respect/severity-error-level/severity-level.yaml @@ -0,0 +1,126 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API + description: >- + An imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api-with-errors.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + parameters: + - in: header + name: Content-Type + value: application/json + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body + - stepId: buy-ticket + description: >- + Buy a ticket for the museum by calling an external workflow from another Arazzo file. + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + outputs: + ticketId: $outputs.ticketId + - workflowId: events-crud + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: list-events + description: >- + Request the list of events. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + - stepId: create-event + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + requestBody: + payload: + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - $steps.list-events.outputs.events.0.dates.0 + - $steps.list-events.outputs.events.0.dates.1 + price: 0 + successCriteria: + - condition: $statusCode == 201 + - context: $response.body + condition: $.name == 'Mermaid Treasure Identification and Analysis' + type: jsonpath + outputs: + createdEventId: $response.body#/eventId + name: $response.body#/name + + - stepId: get-event-by-id + description: >- + Get the details of the event that was created in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/get' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + successCriteria: + - context: $statusCode + condition: '^200$' + type: regex + - stepId: update-event + description: >- + Update the created event with new details. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/patch' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + requestBody: + payload: + name: 'Orca Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 200 + - context: $response.body + condition: $.name == 'Orca Identification and Analysis' + type: + type: jsonpath + version: draft-goessner-dispatch-jsonpath-00 + outputs: + updatedEventId: $response.body#/eventId + - stepId: delete-event + description: >- + Delete the event that was updated in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/delete' + parameters: + - name: eventId + in: path + value: $steps.update-event.outputs.updatedEventId + successCriteria: + - condition: $statusCode == 200 diff --git a/__tests__/respect/severity-off-level/__snapshots__/severity-off-level.test.ts.snap b/__tests__/respect/severity-off-level/__snapshots__/severity-off-level.test.ts.snap new file mode 100644 index 0000000000..e8e36ccb67 --- /dev/null +++ b/__tests__/respect/severity-off-level/__snapshots__/severity-off-level.test.ts.snap @@ -0,0 +1,386 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should use off severity level 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow severity-level.yaml / get-museum-hours + + ✗ GET /museum-hours - step get-museum-hours + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/museum-hours +    Request Headers: +      accept: html/text, application/problem+json +      authorization: Basic Og== +      content-type: application/json + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "date": "2023-09-11", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-12", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-13", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-14", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-15", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       }, +       { +       "date": "2023-09-18", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-19", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-20", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-21", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-22", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       } +      ] + +    ✓ success criteria check +    ✗ status code check + + Running step buy-ticket workflow $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + + ✓ POST /tickets - step buy-tickets +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow severity-level.yaml / events-crud + + ✓ GET /special-events - step list-events + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "eventId": "f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97", +       "name": "Sasquatch Ballet", +       "location": "Seattle... probably", +       "eventDescription": "They're big, they're hairy, but they're also graceful. Come learn how the biggest feet can have the lightest touch.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 40 +       }, +       { +       "eventId": "2f14374a-9c65-4ee5-94b7-fba66d893483", +       "name": "Solar Telescope Demonstration", +       "location": "Far from the sun.", +       "eventDescription": "Look at the sun without going blind!", +       "dates": [ +       "2023-09-07", +       "2023-09-14" +       ], +       "price": 50 +       }, +       { +       "eventId": "6aaa61ba-b2aa-4868-b803-603dbbf7bfdb", +       "name": "Cook like a Caveman", +       "location": "Fire Pit on East side", +       "eventDescription": "Learn to cook on an open flame.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       }, +       { +       "eventId": "602b75e1-5696-4ab8-8c7a-f9e13580f910", +       "name": "Underwater Basket Weaving", +       "location": "Rec Center Pool next door.", +       "eventDescription": "Learn to weave baskets underwater.", +       "dates": [ +       "2023-09-12", +       "2023-09-15" +       ], +       "price": 15 +       }, +       { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Room Sea-12", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 30 +       }, +       { +       "eventId": "6744a0da-4121-49cd-8479-f8cc20526495", +       "name": "Time Traveler Tea Party", +       "location": "Temporal Tearoom", +       "eventDescription": "Sip tea with important historical figures.", +       "dates": [ +       "2023-11-18", +       "2023-11-25", +       "2023-12-02" +       ], +       "price": 60 +       }, +       { +       "eventId": "3be6453c-03eb-4357-ae5a-984a0e574a54", +       "name": "Pirate Coding Workshop", +       "location": "Computer Room", +       "eventDescription": "Captain Blackbeard shares his love of the C...language. And possibly Arrrrr (R lang).", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "9d90d29a-2af5-4206-97d9-9ea9ceadcb78", +       "name": "Llama Street Art Through the Ages", +       "location": "Auditorium", +       "eventDescription": "Llama street art?! Alpaca my bags -- let's go!", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "a3c7b2c4-b5fb-4ef7-9322-00a919864957", +       "name": "The Great Parrot Debate", +       "location": "Outdoor Amphitheatre", +       "eventDescription": "See leading parrot minds discuss important geopolitical issues.", +       "dates": [ +       "2023-11-03", +       "2023-11-10" +       ], +       "price": 35 +       }, +       { +       "eventId": "b92d46b7-4c5d-422b-87a5-287767e26f29", +       "name": "Eat a Bunch of Corn", +       "location": "Cafeteria", +       "eventDescription": "We accidentally bought too much corn. Please come eat it.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       } +      ] + +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✓ POST /special-events - step create-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      content-type: application/json +      accept: application/json, application/problem+json +      authorization: Basic Og== +    Request Body: +      { +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 0 +      } + + +    Response status code: 201 +    Response time: ms +    Response Body: +      { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 0 +      } + +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✓ GET /special-events/{eventId} - step get-event-by-id + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events/dad4bce8-f5cb-4078-a211-995864315e39 +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      { +       "eventId": "6744a0da-4121-49cd-8479-f8cc20526495", +       "name": "Time Traveler Tea Party", +       "location": "Temporal Tearoom", +       "eventDescription": "Sip tea with important historical figures.", +       "dates": [ +       "2023-11-18", +       "2023-11-25", +       "2023-12-02" +       ], +       "price": 60 +      } + +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✗ PATCH /special-events/{eventId} - step update-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events/dad4bce8-f5cb-4078-a211-995864315e39 +    Request Headers: +      content-type: application/json +      accept: json, application/problem+json +      authorization: Basic Og== +    Request Body: +      { +       "name": "Orca Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + + +    Response status code: 200 +    Response time: ms +    Response Body: +      { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Orca Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✗ content-type check + + ✗ DELETE /special-events/{eventId} - step delete-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events/dad4bce8-f5cb-4078-a211-995864315e39 +    Request Headers: +      accept: application/problem+json +      authorization: Basic Og== + + +    Response status code: 204 +    Response time: ms +    Response Body: +      {} + +    ✗ success criteria check +    ✗ status code check + + +  Failed tests info: + +  Workflow name: get-museum-hours + +    stepId - get-museum-hours +    ○ status code check (skipped) +       +       +  Workflow name: events-crud + +    stepId - update-event +    ○ content-type check (skipped) +      Content type application/json for 200 response is not described in the schema. +      Expected content types: json. +       +    stepId - delete-event +    ○ success criteria check (skipped) +      Checking simple criteria: {"condition":"$statusCode == 200"} +       +    ○ status code check (skipped) +       +       +  Summary for severity-level.yaml +   +  Workflows: 2 passed, 2 total +  Steps: 7 passed, 3 ignored, 7 total +  Checks: 24 passed, 4 ignored, 24 total +  Time: ms + + +┌─────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├─────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ ✓ severity-level.yaml │ 2 │ 2 │ - │ - │ 2 │ +└─────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + +" +`; diff --git a/__tests__/respect/severity-off-level/museum-api-with-errors.yaml b/__tests__/respect/severity-off-level/museum-api-with-errors.yaml new file mode 100644 index 0000000000..98fcaf666a --- /dev/null +++ b/__tests__/respect/severity-off-level/museum-api-with-errors.yaml @@ -0,0 +1,755 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '201': + description: Success. + content: + html/text: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '205': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + sample: + type: string + example: 'sample' + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/severity-off-level/museum-api.yaml b/__tests__/respect/severity-off-level/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/severity-off-level/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/severity-off-level/museum-tickets.yaml b/__tests__/respect/severity-off-level/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/severity-off-level/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/severity-off-level/severity-level.yaml b/__tests__/respect/severity-off-level/severity-level.yaml new file mode 100644 index 0000000000..65aae58dc9 --- /dev/null +++ b/__tests__/respect/severity-off-level/severity-level.yaml @@ -0,0 +1,126 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API + description: >- + An imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api-with-errors.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + parameters: + - in: header + name: Content-Type + value: application/json + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body + - stepId: buy-ticket + description: >- + Buy a ticket for the museum by calling an external workflow from another Arazzo file. + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + outputs: + ticketId: $outputs.ticketId + - workflowId: events-crud + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: list-events + description: >- + Request the list of events. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + - stepId: create-event + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + requestBody: + payload: + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - $steps.list-events.outputs.events.0.dates.0 + - $steps.list-events.outputs.events.0.dates.1 + price: 0 + successCriteria: + - condition: $statusCode == 201 + - context: $response.body + condition: $.name == 'Mermaid Treasure Identification and Analysis' + type: jsonpath + outputs: + createdEventId: $response.body#/eventId + name: $response.body#/name + + - stepId: get-event-by-id + description: >- + Get the details of the event that was created in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/get' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + successCriteria: + - context: $statusCode + condition: '^200$' + type: regex + - stepId: update-event + description: >- + Update the created event with new details. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/patch' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + requestBody: + payload: + name: 'Orca Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 200 + - context: $response.body + condition: $.name == 'Orca Identification and Analysis' + type: + type: jsonpath + version: draft-goessner-dispatch-jsonpath-00 + outputs: + updatedEventId: $response.body#/eventId + - stepId: delete-event + description: >- + Delete the event that was updated in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/delete' + parameters: + - name: eventId + in: path + value: $steps.update-event.outputs.updatedEventId + successCriteria: + - condition: $statusCode == 200 diff --git a/__tests__/respect/severity-off-level/severity-off-level.test.ts b/__tests__/respect/severity-off-level/severity-off-level.test.ts new file mode 100644 index 0000000000..14f3e3be5e --- /dev/null +++ b/__tests__/respect/severity-off-level/severity-off-level.test.ts @@ -0,0 +1,23 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +test('should use off severity level', () => { + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'severity-level.yaml'); + const args = getParams(indexEntryPoint, [ + 'respect', + fixturesPath, + '--verbose', + '--severity', + 'STATUS_CODE_CHECK=off', + '--severity', + 'SCHEMA_CHECK=off', + '--severity', + 'SUCCESS_CRITERIA_CHECK=off', + '--severity', + 'CONTENT_TYPE_CHECK=off', + ]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); +}); diff --git a/__tests__/respect/severity-warn-level/__snapshots__/severity-warn-level.test.ts.snap b/__tests__/respect/severity-warn-level/__snapshots__/severity-warn-level.test.ts.snap new file mode 100644 index 0000000000..5e18afb52e --- /dev/null +++ b/__tests__/respect/severity-warn-level/__snapshots__/severity-warn-level.test.ts.snap @@ -0,0 +1,386 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should use warn severity level 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow severity-level.yaml / get-museum-hours + + ✗ GET /museum-hours - step get-museum-hours + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/museum-hours +    Request Headers: +      accept: html/text, application/problem+json +      authorization: Basic Og== +      content-type: application/json + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "date": "2023-09-11", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-12", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-13", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-14", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-15", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       }, +       { +       "date": "2023-09-18", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-19", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-20", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-21", +       "timeOpen": "09:00", +       "timeClose": "18:00" +       }, +       { +       "date": "2023-09-22", +       "timeOpen": "10:00", +       "timeClose": "16:00" +       } +      ] + +    ✓ success criteria check +    ⚠ status code check + + Running step buy-ticket workflow $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + + ✓ POST /tickets - step buy-tickets +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow severity-level.yaml / events-crud + + ✓ GET /special-events - step list-events + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      [ +       { +       "eventId": "f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97", +       "name": "Sasquatch Ballet", +       "location": "Seattle... probably", +       "eventDescription": "They're big, they're hairy, but they're also graceful. Come learn how the biggest feet can have the lightest touch.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 40 +       }, +       { +       "eventId": "2f14374a-9c65-4ee5-94b7-fba66d893483", +       "name": "Solar Telescope Demonstration", +       "location": "Far from the sun.", +       "eventDescription": "Look at the sun without going blind!", +       "dates": [ +       "2023-09-07", +       "2023-09-14" +       ], +       "price": 50 +       }, +       { +       "eventId": "6aaa61ba-b2aa-4868-b803-603dbbf7bfdb", +       "name": "Cook like a Caveman", +       "location": "Fire Pit on East side", +       "eventDescription": "Learn to cook on an open flame.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       }, +       { +       "eventId": "602b75e1-5696-4ab8-8c7a-f9e13580f910", +       "name": "Underwater Basket Weaving", +       "location": "Rec Center Pool next door.", +       "eventDescription": "Learn to weave baskets underwater.", +       "dates": [ +       "2023-09-12", +       "2023-09-15" +       ], +       "price": 15 +       }, +       { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Room Sea-12", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 30 +       }, +       { +       "eventId": "6744a0da-4121-49cd-8479-f8cc20526495", +       "name": "Time Traveler Tea Party", +       "location": "Temporal Tearoom", +       "eventDescription": "Sip tea with important historical figures.", +       "dates": [ +       "2023-11-18", +       "2023-11-25", +       "2023-12-02" +       ], +       "price": 60 +       }, +       { +       "eventId": "3be6453c-03eb-4357-ae5a-984a0e574a54", +       "name": "Pirate Coding Workshop", +       "location": "Computer Room", +       "eventDescription": "Captain Blackbeard shares his love of the C...language. And possibly Arrrrr (R lang).", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "9d90d29a-2af5-4206-97d9-9ea9ceadcb78", +       "name": "Llama Street Art Through the Ages", +       "location": "Auditorium", +       "eventDescription": "Llama street art?! Alpaca my bags -- let's go!", +       "dates": [ +       "2023-10-29", +       "2023-10-30", +       "2023-10-31" +       ], +       "price": 45 +       }, +       { +       "eventId": "a3c7b2c4-b5fb-4ef7-9322-00a919864957", +       "name": "The Great Parrot Debate", +       "location": "Outdoor Amphitheatre", +       "eventDescription": "See leading parrot minds discuss important geopolitical issues.", +       "dates": [ +       "2023-11-03", +       "2023-11-10" +       ], +       "price": 35 +       }, +       { +       "eventId": "b92d46b7-4c5d-422b-87a5-287767e26f29", +       "name": "Eat a Bunch of Corn", +       "location": "Cafeteria", +       "eventDescription": "We accidentally bought too much corn. Please come eat it.", +       "dates": [ +       "2023-11-10", +       "2023-11-17", +       "2023-11-24" +       ], +       "price": 5 +       } +      ] + +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✓ POST /special-events - step create-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events +    Request Headers: +      content-type: application/json +      accept: application/json, application/problem+json +      authorization: Basic Og== +    Request Body: +      { +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 0 +      } + + +    Response status code: 201 +    Response time: ms +    Response Body: +      { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Mermaid Treasure Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-12-15", +       "2023-12-22" +       ], +       "price": 0 +      } + +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✓ GET /special-events/{eventId} - step get-event-by-id + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events/dad4bce8-f5cb-4078-a211-995864315e39 +    Request Headers: +      accept: application/json, application/problem+json +      authorization: Basic Og== + + +    Response status code: 200 +    Response time: ms +    Response Body: +      { +       "eventId": "6744a0da-4121-49cd-8479-f8cc20526495", +       "name": "Time Traveler Tea Party", +       "location": "Temporal Tearoom", +       "eventDescription": "Sip tea with important historical figures.", +       "dates": [ +       "2023-11-18", +       "2023-11-25", +       "2023-12-02" +       ], +       "price": 60 +      } + +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + ✗ PATCH /special-events/{eventId} - step update-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events/dad4bce8-f5cb-4078-a211-995864315e39 +    Request Headers: +      content-type: application/json +      accept: json, application/problem+json +      authorization: Basic Og== +    Request Body: +      { +       "name": "Orca Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + + +    Response status code: 200 +    Response time: ms +    Response Body: +      { +       "eventId": "dad4bce8-f5cb-4078-a211-995864315e39", +       "name": "Orca Identification and Analysis", +       "location": "Under the seaaa 🦀 🎶 🌊.", +       "eventDescription": "Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.", +       "dates": [ +       "2023-09-05", +       "2023-09-08" +       ], +       "price": 0 +      } + +    ✓ success criteria check +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ⚠ content-type check + + ✗ DELETE /special-events/{eventId} - step delete-event + +    Request URL: https://redocly.com/_mock/demo/openapi/museum-api/special-events/dad4bce8-f5cb-4078-a211-995864315e39 +    Request Headers: +      accept: application/problem+json +      authorization: Basic Og== + + +    Response status code: 204 +    Response time: ms +    Response Body: +      {} + +    ⚠ success criteria check +    ⚠ status code check + + +  Failed tests info: + +  Workflow name: get-museum-hours + +    stepId - get-museum-hours +    ⚠ status code check +       +       +  Workflow name: events-crud + +    stepId - update-event +    ⚠ content-type check +      Content type application/json for 200 response is not described in the schema. +      Expected content types: json. +       +    stepId - delete-event +    ⚠ success criteria check +      Checking simple criteria: {"condition":"$statusCode == 200"} +       +    ⚠ status code check +       +       +  Summary for severity-level.yaml +   +  Workflows: 2 passed, 2 total +  Steps: 7 passed, 3 warnings, 7 total +  Checks: 24 passed, 4 warnings, 24 total +  Time: ms + + +┌─────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├─────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ ✓ severity-level.yaml │ 2 │ 2 │ - │ 2 │ - │ +└─────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + +" +`; diff --git a/__tests__/respect/severity-warn-level/museum-api-with-errors.yaml b/__tests__/respect/severity-warn-level/museum-api-with-errors.yaml new file mode 100644 index 0000000000..98fcaf666a --- /dev/null +++ b/__tests__/respect/severity-warn-level/museum-api-with-errors.yaml @@ -0,0 +1,755 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '201': + description: Success. + content: + html/text: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '205': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + sample: + type: string + example: 'sample' + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/severity-warn-level/museum-api.yaml b/__tests__/respect/severity-warn-level/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/severity-warn-level/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/severity-warn-level/museum-tickets.yaml b/__tests__/respect/severity-warn-level/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/severity-warn-level/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/severity-warn-level/severity-level.yaml b/__tests__/respect/severity-warn-level/severity-level.yaml new file mode 100644 index 0000000000..65aae58dc9 --- /dev/null +++ b/__tests__/respect/severity-warn-level/severity-level.yaml @@ -0,0 +1,126 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API + description: >- + An imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api-with-errors.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + parameters: + - in: header + name: Content-Type + value: application/json + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body + - stepId: buy-ticket + description: >- + Buy a ticket for the museum by calling an external workflow from another Arazzo file. + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + outputs: + ticketId: $outputs.ticketId + - workflowId: events-crud + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: list-events + description: >- + Request the list of events. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + - stepId: create-event + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + requestBody: + payload: + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - $steps.list-events.outputs.events.0.dates.0 + - $steps.list-events.outputs.events.0.dates.1 + price: 0 + successCriteria: + - condition: $statusCode == 201 + - context: $response.body + condition: $.name == 'Mermaid Treasure Identification and Analysis' + type: jsonpath + outputs: + createdEventId: $response.body#/eventId + name: $response.body#/name + + - stepId: get-event-by-id + description: >- + Get the details of the event that was created in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/get' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + successCriteria: + - context: $statusCode + condition: '^200$' + type: regex + - stepId: update-event + description: >- + Update the created event with new details. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/patch' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + requestBody: + payload: + name: 'Orca Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 200 + - context: $response.body + condition: $.name == 'Orca Identification and Analysis' + type: + type: jsonpath + version: draft-goessner-dispatch-jsonpath-00 + outputs: + updatedEventId: $response.body#/eventId + - stepId: delete-event + description: >- + Delete the event that was updated in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/delete' + parameters: + - name: eventId + in: path + value: $steps.update-event.outputs.updatedEventId + successCriteria: + - condition: $statusCode == 200 diff --git a/__tests__/respect/severity-warn-level/severity-warn-level.test.ts b/__tests__/respect/severity-warn-level/severity-warn-level.test.ts new file mode 100644 index 0000000000..6cc5c74f85 --- /dev/null +++ b/__tests__/respect/severity-warn-level/severity-warn-level.test.ts @@ -0,0 +1,23 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +test('should use warn severity level', () => { + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'severity-level.yaml'); + const args = getParams(indexEntryPoint, [ + 'respect', + fixturesPath, + '--verbose', + '--severity', + 'STATUS_CODE_CHECK=warn', + '--severity', + 'SCHEMA_CHECK=warn', + '--severity', + 'SUCCESS_CRITERIA_CHECK=warn', + '--severity', + 'CONTENT_TYPE_CHECK=warn', + ]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); +}); diff --git a/__tests__/respect/step-on-failure-type-end-action/__snapshots__/step-on-failure-type-end-action.test.ts.snap b/__tests__/respect/step-on-failure-type-end-action/__snapshots__/step-on-failure-type-end-action.test.ts.snap new file mode 100644 index 0000000000..a1bc9aa679 --- /dev/null +++ b/__tests__/respect/step-on-failure-type-end-action/__snapshots__/step-on-failure-type-end-action.test.ts.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should end workflow execution, context returns to the caller with applicable outputs, when step fails and onFailure action is of type \`end\` 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow step-on-failure-type-end-action.yaml / events-crud + + ✗ GET /special-events - step list-events +    ✗ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow step-on-failure-type-end-action.yaml / get-museum-hours + + ✓ GET /museum-hours - step get-museum-hours +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +  Failed tests info: + +  Workflow name: events-crud + +    stepId - list-events +    ✗ success criteria check +      Checking simple criteria: {"condition":"$statusCode == 201"} +       +  Summary for step-on-failure-type-end-action.yaml +   +  Workflows: 1 passed, 1 failed, 2 total +  Steps: 2 passed, 1 failed, 3 total +  Checks: 7 passed, 1 failed, 8 total +  Time: ms + + +┌──────────────────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├──────────────────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ x step-on-failure-type-end-action.yaml │ 2 │ 1 │ 1 │ - │ - │ +└──────────────────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + + + Tests exited with error +" +`; diff --git a/__tests__/respect/step-on-failure-type-end-action/museum-api.yaml b/__tests__/respect/step-on-failure-type-end-action/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/step-on-failure-type-end-action/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/step-on-failure-type-end-action/museum-tickets.yaml b/__tests__/respect/step-on-failure-type-end-action/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/step-on-failure-type-end-action/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/step-on-failure-type-end-action/step-on-failure-type-end-action.test.ts b/__tests__/respect/step-on-failure-type-end-action/step-on-failure-type-end-action.test.ts new file mode 100644 index 0000000000..7a74c1ac95 --- /dev/null +++ b/__tests__/respect/step-on-failure-type-end-action/step-on-failure-type-end-action.test.ts @@ -0,0 +1,15 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; +// Snapshot should have two workflows, and first workflow should run only first step +test('should end workflow execution, context returns to the caller with applicable outputs, when step fails and onFailure action is of type `end`', () => { + process.env.AUTH_TOKEN = 'Basic Og=='; + + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'step-on-failure-type-end-action.yaml'); + const args = getParams(indexEntryPoint, ['respect', fixturesPath]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); + + delete process.env.AUTH_TOKEN; +}); diff --git a/__tests__/respect/step-on-failure-type-end-action/step-on-failure-type-end-action.yaml b/__tests__/respect/step-on-failure-type-end-action/step-on-failure-type-end-action.yaml new file mode 100644 index 0000000000..f0a60643b6 --- /dev/null +++ b/__tests__/respect/step-on-failure-type-end-action/step-on-failure-type-end-action.yaml @@ -0,0 +1,81 @@ +arazzo: 1.0.1 +info: + title: Test `successActions` in workflows + description: Testing functionality of `end` action in `onFailure` action. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: events-crud + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: list-events + description: >- + Request the list of events. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + successCriteria: + - condition: $statusCode == 201 + onFailure: + - type: end + name: step-onFailure-action-end + criteria: + - condition: $statusCode == 200 + # This goto action should not be called because previous action is an end action. + - type: goto + name: step-onFailure-action-goto + stepId: create-event + - type: retry + name: step-onFailure-action-retry + retryLimit: 1 + retryAfter: 3000 + - stepId: create-event + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + onSuccess: + # This end action should not be called because the status code is 201 and the criteria are not met + - type: end + name: step-onSuccess-action-end + criteria: + - condition: $statusCode == 403 + requestBody: + payload: + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 201 + - workflowId: get-museum-hours + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: $sourceDescriptions.museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body diff --git a/__tests__/respect/step-on-success-type-end-action/__snapshots__/step-on-success-type-end-action.test.ts.snap b/__tests__/respect/step-on-success-type-end-action/__snapshots__/step-on-success-type-end-action.test.ts.snap new file mode 100644 index 0000000000..744cef3e55 --- /dev/null +++ b/__tests__/respect/step-on-success-type-end-action/__snapshots__/step-on-success-type-end-action.test.ts.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should end workflow execution, context returns to the caller with applicable outputs, when step passes and onSuccess action is of type \`end\` 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow step-on-success-type-end-action.yaml / events-crud + + ✓ GET /special-events - step list-events +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow step-on-success-type-end-action.yaml / get-museum-hours + + ✓ GET /museum-hours - step get-museum-hours +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +  Summary for step-on-success-type-end-action.yaml +   +  Workflows: 2 passed, 2 total +  Steps: 3 passed, 3 total +  Checks: 8 passed, 8 total +  Time: ms + + +┌──────────────────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├──────────────────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ ✓ step-on-success-type-end-action.yaml │ 2 │ 2 │ - │ - │ - │ +└──────────────────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + +" +`; diff --git a/__tests__/respect/step-on-success-type-end-action/museum-api.yaml b/__tests__/respect/step-on-success-type-end-action/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/step-on-success-type-end-action/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/step-on-success-type-end-action/museum-tickets.yaml b/__tests__/respect/step-on-success-type-end-action/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/step-on-success-type-end-action/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/step-on-success-type-end-action/step-on-success-type-end-action.test.ts b/__tests__/respect/step-on-success-type-end-action/step-on-success-type-end-action.test.ts new file mode 100644 index 0000000000..822ae8b4ca --- /dev/null +++ b/__tests__/respect/step-on-success-type-end-action/step-on-success-type-end-action.test.ts @@ -0,0 +1,16 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +// Snapshot should have two workflows, and first workflow should run only first step +test('should end workflow execution, context returns to the caller with applicable outputs, when step passes and onSuccess action is of type `end`', () => { + process.env.AUTH_TOKEN = 'Basic Og=='; + + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'step-on-success-type-end-action.yaml'); + const args = getParams(indexEntryPoint, ['respect', fixturesPath]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); + + delete process.env.AUTH_TOKEN; +}); diff --git a/__tests__/respect/step-on-success-type-end-action/step-on-success-type-end-action.yaml b/__tests__/respect/step-on-success-type-end-action/step-on-success-type-end-action.yaml new file mode 100644 index 0000000000..d319d9a5c5 --- /dev/null +++ b/__tests__/respect/step-on-success-type-end-action/step-on-success-type-end-action.yaml @@ -0,0 +1,71 @@ +arazzo: 1.0.1 +info: + title: Test `successActions` in workflows + description: Testing functionality of `end` action in `onSuccess` action. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: events-crud + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: list-events + description: >- + Request the list of events. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + successCriteria: + - condition: $statusCode == 200 + onSuccess: + - type: end + name: step-onSuccess-action-end + - stepId: create-event + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + onSuccess: + - type: goto + name: step-onSuccess-action-2 + stepId: list-events + criteria: + - condition: $statusCode == 201 + requestBody: + payload: + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 201 + - workflowId: get-museum-hours + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: $sourceDescriptions.museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body diff --git a/__tests__/respect/utils.ts b/__tests__/respect/utils.ts new file mode 100644 index 0000000000..bf3fd788ed --- /dev/null +++ b/__tests__/respect/utils.ts @@ -0,0 +1,36 @@ +import { spawnSync } from 'child_process'; +import { join } from 'path'; + +const fixturesPath = join(__dirname, 'apis'); + +export function getParams(indexEntryPoint: string, args: string[] = []): string[] { + return [indexEntryPoint, ...args]; +} + +export function getCommandOutput(args: string[]) { + const result = spawnSync('node', args, { + encoding: 'utf-8', + stdio: 'pipe', + env: { + ...process.env, + NODE_ENV: 'test', + NO_COLOR: 'TRUE', + FORCE_COLOR: '0', + }, + }); + + if (result.error) { + throw new Error(`Command execution failed: ${result.error.message}`); + } + + const out = result.stdout ? result.stdout.toString() : ''; + const err = result.stderr ? result.stderr.toString() : ''; + return `${out}\n${err}`; +} + +export const getFixturePath = (fileName: string): string => join(fixturesPath, fileName); + +export function cleanColors(input: string): string { + // eslint-disable-next-line no-control-regex + return input.replace(/\x1b\[\d+m/g, ''); +} diff --git a/__tests__/respect/workflow-failure-actions/__snapshots__/workflow-failure-actions.test.ts.snap b/__tests__/respect/workflow-failure-actions/__snapshots__/workflow-failure-actions.test.ts.snap new file mode 100644 index 0000000000..065d34625d --- /dev/null +++ b/__tests__/respect/workflow-failure-actions/__snapshots__/workflow-failure-actions.test.ts.snap @@ -0,0 +1,64 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should execute successActions for each workflow step if it does not have onSuccess action itself 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow workflow-failure-actions.yaml / get-museum-hours + + Retrying step get-museum-hours attempt # 1 + + Running required workflows for get-museum-hours  + + ✓ POST /tickets - step buy-tickets +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + + ✗ GET /museum-hours - step get-museum-hours +    ✗ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + Retrying step get-museum-hours-2 attempt # 1 + + ✗ GET /museum-hours - step get-museum-hours-2 +    ✗ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +  Failed tests info: + +  Workflow name: get-museum-hours + +    stepId - get-museum-hours +    ✗ success criteria check +      Checking simple criteria: {"condition":"$statusCode == 201"} +       +    stepId - get-museum-hours-2 +    ✗ success criteria check +      Checking simple criteria: {"condition":"$statusCode == 201"} +       +  Summary for workflow-failure-actions.yaml +   +  Workflows: 1 failed, 1 total +  Steps: 2 failed, 2 total +  Checks: 6 passed, 2 failed, 8 total +  Time: ms + + +┌───────────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├───────────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ x workflow-failure-actions.yaml │ 1 │ 0 │ 1 │ - │ - │ +└───────────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + + + Tests exited with error +" +`; diff --git a/__tests__/respect/workflow-failure-actions/museum-api.yaml b/__tests__/respect/workflow-failure-actions/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/workflow-failure-actions/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/workflow-failure-actions/museum-tickets.yaml b/__tests__/respect/workflow-failure-actions/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/workflow-failure-actions/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/workflow-failure-actions/workflow-failure-actions.test.ts b/__tests__/respect/workflow-failure-actions/workflow-failure-actions.test.ts new file mode 100644 index 0000000000..fd857b3069 --- /dev/null +++ b/__tests__/respect/workflow-failure-actions/workflow-failure-actions.test.ts @@ -0,0 +1,11 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +test('should execute successActions for each workflow step if it does not have onSuccess action itself', () => { + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'workflow-failure-actions.yaml'); + const args = getParams(indexEntryPoint, ['respect', fixturesPath]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); +}); diff --git a/__tests__/respect/workflow-failure-actions/workflow-failure-actions.yaml b/__tests__/respect/workflow-failure-actions/workflow-failure-actions.yaml new file mode 100644 index 0000000000..e95147cf06 --- /dev/null +++ b/__tests__/respect/workflow-failure-actions/workflow-failure-actions.yaml @@ -0,0 +1,62 @@ +arazzo: 1.0.1 +info: + title: Test `failureActions` in workflows + description: Testing functionality based on MuseumAPI example using mockserver + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + failureActions: + - name: repeated-failure-action-1 + type: retry + retryLimit: 1 + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + criteria: + - condition: $statusCode == 200 + - name: repeated-failure-action-2 # this should not be executed as there is another action with the s + type: goto + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + criteria: + - condition: $statusCode == 200 + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: $sourceDescriptions.museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 201 + outputs: + schedule: $response.body + - stepId: get-museum-hours-2 + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: $sourceDescriptions.museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 201 + outputs: + schedule: $response.body + onFailure: + - type: retry + name: not-executed-retry + retryLimit: 1 + criteria: + - condition: $statusCode == 999 + - type: retry + name: step-onFailure-action + retryLimit: 1 + criteria: + - condition: $statusCode == 200 diff --git a/__tests__/respect/workflow-success-actions/__snapshots__/workflow-success-actions.test.ts.snap b/__tests__/respect/workflow-success-actions/__snapshots__/workflow-success-actions.test.ts.snap new file mode 100644 index 0000000000..4043d40869 --- /dev/null +++ b/__tests__/respect/workflow-success-actions/__snapshots__/workflow-success-actions.test.ts.snap @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should execute successActions for each workflow step if it does not have onSuccess action itself 1`] = ` +"──────────────────────────────────────────────────────────────────────────────── + + Running workflow workflow-success-actions.yaml / get-museum-hours + + ✓ GET /museum-hours - step get-museum-hours +    ✓ success criteria check +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + Running required workflows for get-museum-hours  + + ✓ POST /tickets - step buy-tickets +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + +──────────────────────────────────────────────────────────────────────────────── + + Running workflow workflow-success-actions.yaml / events-crud + + ✓ GET /special-events - step list-events +    ✓ status code check (Response code 200 matches one of description codes: [200, 400, 404]) +    ✓ content-type check +    ✓ schema check + + Running required workflows for events-crud  + + ✓ POST /tickets - step buy-tickets +    ✓ success criteria check +    ✓ status code check (Response code 201 matches one of description codes: [201, 400, 404]) +    ✓ content-type check +    ✓ schema check + + + +  Summary for workflow-success-actions.yaml +   +  Workflows: 2 passed, 2 total +  Steps: 3 passed, 3 total +  Checks: 7 passed, 7 total +  Time: ms + + +┌───────────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┬─────────┐ +│ Filename │ Workflows │ Passed │ Failed │ Warnings │ Skipped │ +├───────────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┼─────────┤ +│ ✓ workflow-success-actions.yaml │ 2 │ 2 │ - │ - │ - │ +└───────────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┴─────────┘ + + +" +`; \ No newline at end of file diff --git a/__tests__/respect/workflow-success-actions/museum-api.yaml b/__tests__/respect/workflow-success-actions/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/__tests__/respect/workflow-success-actions/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/__tests__/respect/workflow-success-actions/museum-tickets.yaml b/__tests__/respect/workflow-success-actions/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/__tests__/respect/workflow-success-actions/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/__tests__/respect/workflow-success-actions/workflow-success-actions.test.ts b/__tests__/respect/workflow-success-actions/workflow-success-actions.test.ts new file mode 100644 index 0000000000..bc820b51cd --- /dev/null +++ b/__tests__/respect/workflow-success-actions/workflow-success-actions.test.ts @@ -0,0 +1,11 @@ +import { getParams, getCommandOutput } from '../utils'; +import { join } from 'path'; + +test('should execute successActions for each workflow step if it does not have onSuccess action itself', () => { + const indexEntryPoint = join(process.cwd(), 'packages/cli/lib/index.js'); + const fixturesPath = join(__dirname, 'workflow-success-actions.yaml'); + const args = getParams(indexEntryPoint, ['respect', fixturesPath]); + + const result = getCommandOutput(args); + expect(result).toMatchSnapshot(); +}); diff --git a/__tests__/respect/workflow-success-actions/workflow-success-actions.yaml b/__tests__/respect/workflow-success-actions/workflow-success-actions.yaml new file mode 100644 index 0000000000..80c41f41d2 --- /dev/null +++ b/__tests__/respect/workflow-success-actions/workflow-success-actions.yaml @@ -0,0 +1,88 @@ +arazzo: 1.0.1 +info: + title: Test `successActions` in workflows + description: Testing functionality based on MuseumAPI example using mockserver + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + successActions: + - name: repeated-success-action-1 + type: goto + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + criteria: + - condition: $statusCode == 200 + - name: repeated-success-action-2 # this should not be executed as there is another action with the same criteria + type: goto + workflowId: events-crud + criteria: + - condition: $statusCode == 200 + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: $sourceDescriptions.museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body + - workflowId: events-crud + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: list-events + description: >- + Request the list of events. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + onSuccess: + - type: goto + name: step-onSuccess-action + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + criteria: + - condition: $statusCode == 200 + - stepId: create-event + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + onSuccess: + - type: goto + name: step-onSuccess-action-2 + stepId: list-events + criteria: + - condition: $statusCode == 201 + requestBody: + payload: + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 201 + - context: $response.body + condition: $.name == 'Mermaid Treasure Identification and Analysis' + type: + type: jsonpath + version: draft-goessner-dispatch-jsonpath-00 diff --git a/__tests__/smoke-plugins/prepare-smoke-plugins.sh b/__tests__/smoke-plugins/prepare-smoke-plugins.sh index c6903005d5..ddd33d7262 100644 --- a/__tests__/smoke-plugins/prepare-smoke-plugins.sh +++ b/__tests__/smoke-plugins/prepare-smoke-plugins.sh @@ -3,7 +3,7 @@ # For npm (Mutates packages/cli/package.json) npm run pack:prepare -cp ./redocly-cli.tgz ./openapi-core.tgz ./__tests__/smoke-plugins +cp ./redocly-cli.tgz ./openapi-core.tgz ./respect-core.tgz ./__tests__/smoke-plugins echo "Current directory:" pwd diff --git a/__tests__/smoke/prepare-smoke.sh b/__tests__/smoke/prepare-smoke.sh index 657bb2ab6d..4ec7e258a0 100644 --- a/__tests__/smoke/prepare-smoke.sh +++ b/__tests__/smoke/prepare-smoke.sh @@ -6,7 +6,7 @@ npm run webpack-bundle # For npm (Mutates packages/cli/package.json) npm run pack:prepare -cp ./redocly-cli.tgz ./openapi-core.tgz ./dist/bundle.js resources/pets.yaml resources/museum.yaml ./__tests__/smoke/ +cp ./redocly-cli.tgz ./openapi-core.tgz ./respect-core.tgz ./dist/bundle.js resources/pets.yaml resources/museum.yaml ./__tests__/smoke/ echo "Current directory:" pwd diff --git a/jest.config.js b/jest.config.js index 4a3d1b0475..2b5aafe4d3 100644 --- a/jest.config.js +++ b/jest.config.js @@ -23,6 +23,12 @@ module.exports = { functions: 63, lines: 64, }, + 'packages/respect-core/': { + statements: 84, + branches: 74, + functions: 84, + lines: 85, + }, }, testMatch: ['**/__tests__/**/*.test.ts', '**/*.test.ts'], globals: { diff --git a/package-lock.json b/package-lock.json index b9b4f01ad4..c3c84bb81f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,8 +24,10 @@ "jest": "^29.0.0", "jest-environment-jsdom": "^29.7.0", "json-schema-to-ts": "^3.0.0", + "json-server": "1.0.0-beta.3", "null-loader": "^4.0.0", "outdent": "^0.7.1", + "pegjs": "0.10.0", "prettier": "^2.1.2", "slackify-markdown": "^4.3.1", "ts-jest": "^29.1.1", @@ -2353,6 +2355,18 @@ "prettier": "^2.7.1" } }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -2380,6 +2394,380 @@ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" }, + "node_modules/@esbuild/android-arm": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.15.tgz", + "integrity": "sha512-sRSOVlLawAktpMvDyJIkdLI/c/kdRTOqo8t6ImVxg8yT7LQDUYV5Rp2FKeEosLr6ZCja9UjYAzyRSxGteSJPYg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.15.tgz", + "integrity": "sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.15.tgz", + "integrity": "sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.15.tgz", + "integrity": "sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.15.tgz", + "integrity": "sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.15.tgz", + "integrity": "sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.15.tgz", + "integrity": "sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.15.tgz", + "integrity": "sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.15.tgz", + "integrity": "sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.15.tgz", + "integrity": "sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.15.tgz", + "integrity": "sha512-k7FsUJjGGSxwnBmMh8d7IbObWu+sF/qbwc+xKZkBe/lTAF16RqxRCnNHA7QTd3oS2AfGBAnHlXL67shV5bBThQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.15.tgz", + "integrity": "sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.15.tgz", + "integrity": "sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.15.tgz", + "integrity": "sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.15.tgz", + "integrity": "sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.15.tgz", + "integrity": "sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.15.tgz", + "integrity": "sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.15.tgz", + "integrity": "sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.15.tgz", + "integrity": "sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.15.tgz", + "integrity": "sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.15.tgz", + "integrity": "sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.15.tgz", + "integrity": "sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -2451,6 +2839,16 @@ "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.2.4.tgz", "integrity": "sha512-DNmuVYeOqFmLmJIJvFIX1TKttOZVI9FwDrqDujhyArjqtXUaZuuB+SuDBTQq3Ev368a7ONJiAJ8m9zi0+IBqZQ==" }, + "node_modules/@faker-js/faker": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-7.6.0.tgz", + "integrity": "sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==", + "license": "MIT", + "engines": { + "node": ">=14.0.0", + "npm": ">=6.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", @@ -2497,12 +2895,124 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@humanwhocodes/momoa": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-2.0.4.tgz", + "integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.10.0" + } + }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2739,7 +3249,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, "dependencies": { "jest-get-type": "^29.6.3" }, @@ -2751,7 +3260,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -2835,7 +3343,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -3002,6 +3509,30 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, "node_modules/@manypkg/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", @@ -3408,6 +3939,12 @@ "node": ">=14" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "dev": true + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -3454,75 +3991,342 @@ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", "license": "BSD-3-Clause" }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@redocly/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@redocly/cli": { + "resolved": "packages/cli", + "link": true + }, + "node_modules/@redocly/config": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.20.1.tgz", + "integrity": "sha512-TYiTDtuItiv95YMsrRxyCs1HKLrDPtTvpaD3+kDKXBnFDeJuYKZ+eHXpCr6YeN4inxfVBs7DLhHsQcs9srddyQ==", + "license": "MIT" + }, + "node_modules/@redocly/openapi-core": { + "resolved": "packages/core", + "link": true + }, + "node_modules/@redocly/respect-core": { + "resolved": "packages/respect-core", + "link": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tinyhttp/accepts": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/accepts/-/accepts-2.2.3.tgz", + "integrity": "sha512-9pQN6pJAJOU3McmdJWTcyq7LLFW8Lj5q+DadyKcvp+sxMkEpktKX5sbfJgJuOvjk6+1xWl7pe0YL1US1vaO/1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime": "4.0.4", + "negotiator": "^0.6.3" + }, + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/app": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tinyhttp/app/-/app-2.5.0.tgz", + "integrity": "sha512-mwVY6RhTqF/s49tqNqpkRWbXAC7OmfpxR6YI6atp9xDauUOv3n1hv38AKy89Ga8HC8G2p1KR9vPD66VmF6VMnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/cookie": "2.1.1", + "@tinyhttp/proxy-addr": "2.2.0", + "@tinyhttp/req": "2.2.4", + "@tinyhttp/res": "2.2.4", + "@tinyhttp/router": "2.2.3", + "header-range-parser": "1.1.3", + "regexparam": "^2.0.2" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/content-disposition": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-disposition/-/content-disposition-2.2.2.tgz", + "integrity": "sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/content-type": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-type/-/content-type-0.1.4.tgz", + "integrity": "sha512-dl6f3SHIJPYbhsW1oXdrqOmLSQF/Ctlv3JnNfXAE22kIP7FosqJHxkz/qj2gv465prG8ODKH5KEyhBkvwrueKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4" + } + }, + "node_modules/@tinyhttp/cookie": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cookie/-/cookie-2.1.1.tgz", + "integrity": "sha512-h/kL9jY0e0Dvad+/QU3efKZww0aTvZJslaHj3JTPmIPC9Oan9+kYqmh3M6L5JUQRuTJYFK2nzgL2iJtH2S+6dA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/cookie-signature": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cookie-signature/-/cookie-signature-2.1.1.tgz", + "integrity": "sha512-VDsSMY5OJfQJIAtUgeQYhqMPSZptehFSfvEEtxr+4nldPA8IImlp3QVcOVuK985g4AFR4Hl1sCbWCXoqBnVWnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/cors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cors/-/cors-2.0.1.tgz", + "integrity": "sha512-qrmo6WJuaiCzKWagv2yA/kw6hIISfF/hOqPWwmI6w0o8apeTMmRN3DoCFvQ/wNVuWVdU5J4KU7OX8aaSOEq51A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/vary": "^0.1.3" + }, + "engines": { + "node": ">=12.20 || 14.x || >=16" + } + }, + "node_modules/@tinyhttp/encode-url": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/encode-url/-/encode-url-2.1.1.tgz", + "integrity": "sha512-AhY+JqdZ56qV77tzrBm0qThXORbsVjs/IOPgGCS7x/wWnsa/Bx30zDUU/jPAUcSzNOzt860x9fhdGpzdqbUeUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" + "node_modules/@tinyhttp/etag": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/etag/-/etag-2.1.2.tgz", + "integrity": "sha512-j80fPKimGqdmMh6962y+BtQsnYPVCzZfJw0HXjyH70VaJBHLKGF+iYhcKqzI3yef6QBNa8DKIPsbEYpuwApXTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" + "node_modules/@tinyhttp/forwarded": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/forwarded/-/forwarded-2.1.1.tgz", + "integrity": "sha512-nO3kq0R1LRl2+CAMlnggm22zE6sT8gfvGbNvSitV6F9eaUSurHP0A8YZFMihSkugHxK+uIegh1TKrqgD8+lyGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } }, - "node_modules/@redocly/ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", + "node_modules/@tinyhttp/logger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tinyhttp/logger/-/logger-2.0.0.tgz", + "integrity": "sha512-8DfLQjGDIaIJeivYamVrrpmwmsGwS8wt2DGvzlcY5HEBagdiI4QJy/veAFcUHuaJqufn4wLwmn4q5VUkW8BCpQ==", + "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js-replace": "^1.0.1" + "colorette": "^2.0.20", + "dayjs": "^1.11.10", + "http-status-emojis": "^2.2.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=14.18 || >=16.20" } }, - "node_modules/@redocly/cli": { - "resolved": "packages/cli", - "link": true - }, - "node_modules/@redocly/config": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.20.1.tgz", - "integrity": "sha512-TYiTDtuItiv95YMsrRxyCs1HKLrDPtTvpaD3+kDKXBnFDeJuYKZ+eHXpCr6YeN4inxfVBs7DLhHsQcs9srddyQ==", + "node_modules/@tinyhttp/logger/node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, "license": "MIT" }, - "node_modules/@redocly/openapi-core": { - "resolved": "packages/core", - "link": true + "node_modules/@tinyhttp/proxy-addr": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@tinyhttp/proxy-addr/-/proxy-addr-2.2.0.tgz", + "integrity": "sha512-WM/PPL9xNvrs7/8Om5nhKbke5FHrP3EfjOOR+wBnjgESfibqn0K7wdUTnzSLp1lBmemr88os1XvzwymSgaibyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/forwarded": "2.1.1", + "ipaddr.js": "^2.2.0" + }, + "engines": { + "node": ">=12.20.0" + } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true + "node_modules/@tinyhttp/req": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/req/-/req-2.2.4.tgz", + "integrity": "sha512-lQAZIAo0NOeghxFOZS57tQzxpHSPPLs9T68Krq2BncEBImKwqaDKUt7M9Y5Kb+rvC/GwIL3LeErhkg7f5iG4IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/accepts": "2.2.3", + "@tinyhttp/type-is": "2.2.4", + "@tinyhttp/url": "2.1.1", + "header-range-parser": "^1.1.3" + }, + "engines": { + "node": ">=12.20.0" + } }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "node_modules/@tinyhttp/res": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/res/-/res-2.2.4.tgz", + "integrity": "sha512-ETBRShnO19oJyIg2XQHQoofXPWeTXPAuwnIVYkU8WaftvXd/Vz4y5+WFQDHUzKlmdGOw5fAFnrEU7pIVMeFeVA==", "dev": true, + "license": "MIT", "dependencies": { - "type-detect": "4.0.8" + "@tinyhttp/content-disposition": "2.2.2", + "@tinyhttp/cookie": "2.1.1", + "@tinyhttp/cookie-signature": "2.1.1", + "@tinyhttp/encode-url": "2.1.1", + "@tinyhttp/req": "2.2.4", + "@tinyhttp/send": "2.2.3", + "@tinyhttp/vary": "^0.1.3", + "es-escape-html": "^0.1.1", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "node_modules/@tinyhttp/router": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/router/-/router-2.2.3.tgz", + "integrity": "sha512-O0MQqWV3Vpg/uXsMYg19XsIgOhwjyhTYWh51Qng7bxqXixxx2PEvZWnFjP7c84K7kU/nUX41KpkEBTLnznk9/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/send": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/send/-/send-2.2.3.tgz", + "integrity": "sha512-o4cVHHGQ8WjVBS8UT0EE/2WnjoybrfXikHwsRoNlG1pfrC/Sd01u1N4Te8cOd/9aNGLr4mGxWb5qTm2RRtEi7g==", "dev": true, + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@tinyhttp/content-type": "^0.1.4", + "@tinyhttp/etag": "2.1.2", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/type-is": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/type-is/-/type-is-2.2.4.tgz", + "integrity": "sha512-7F328NheridwjIfefBB2j1PEcKKABpADgv7aCJaE8x8EON77ZFrAkI3Rir7pGjopV7V9MBmW88xUQigBEX2rmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/content-type": "^0.1.4", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/url": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/url/-/url-2.1.1.tgz", + "integrity": "sha512-POJeq2GQ5jI7Zrdmj22JqOijB5/GeX+LEX7DUdml1hUnGbJOTWDx7zf2b5cCERj7RoXL67zTgyzVblBJC+NJWg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/vary": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/vary/-/vary-0.1.3.tgz", + "integrity": "sha512-SoL83sQXAGiHN1jm2VwLUWQSQeDAAl1ywOm6T0b0Cg1CZhVsjoiZadmjhxF6FHCCY7OHHVaLnTgSMxTPIDLxMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" } }, "node_modules/@tootallnate/once": { @@ -3575,12 +4379,29 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/concat-stream": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-2.0.3.tgz", + "integrity": "sha512-3qe4oQAPNwVNwK4C9c8u+VJqv9kez+2MR4qJpoPFfXtgxxif1QbFusvXzK0/Wra2VX07smostI2VMmJNSpZjuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/configstore": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/configstore/-/configstore-5.0.1.tgz", "integrity": "sha512-c/QCznvk7bLKGhHETj29rqKufui3jaAxjBhK4R2zUrMG5UG0qTwfWYxBoUbH8JCyDjdCWMIxPJ7/Fdz1UcAnWg==", "dev": true }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -3612,6 +4433,13 @@ "@types/node": "*" } }, + "node_modules/@types/har-format": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz", + "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -3662,10 +4490,11 @@ "dev": true }, "node_modules/@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", - "dev": true + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/jsdom": { "version": "20.0.1", @@ -3678,6 +4507,13 @@ "parse5": "^7.0.0" } }, + "node_modules/@types/json-pointer": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/@types/json-pointer/-/json-pointer-1.0.34.tgz", + "integrity": "sha512-JRnWcxzXSaLei98xgw1B7vAeBVOrkyw0+Rt9j1QoJrczE78OpHsyQC8GNbuhw+/2vxxDe58QvWnngS86CoIbRg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.14", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", @@ -3771,6 +4607,16 @@ "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, + "node_modules/@types/set-cookie-parser": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz", + "integrity": "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/sizzle": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", @@ -4280,7 +5126,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4304,8 +5149,7 @@ "node_modules/ajv/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/ansi-colors": { "version": "4.1.3", @@ -4388,6 +5232,130 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/args": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", + "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "5.0.0", + "chalk": "2.4.2", + "leven": "2.1.0", + "mri": "1.1.4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/args/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/args/node_modules/camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/args/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/args/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/args/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/args/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/args/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/args/node_modules/leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/args/node_modules/mri": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", + "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/args/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -4511,6 +5479,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4706,6 +5681,25 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/better-ajv-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/better-ajv-errors/-/better-ajv-errors-1.2.0.tgz", + "integrity": "sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==", + "license": "Apache-2.0", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@humanwhocodes/momoa": "^2.0.2", + "chalk": "^4.1.2", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0 < 4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "peerDependencies": { + "ajv": "4.11.8 - 8" + } + }, "node_modules/better-path-resolve": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", @@ -4809,8 +5803,22 @@ "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/call-bind": { "version": "1.0.7", @@ -4895,7 +5903,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5072,6 +6079,17 @@ "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -5088,11 +6106,50 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, "node_modules/colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -5115,6 +6172,21 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "node_modules/convert-source-map": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", @@ -5123,6 +6195,15 @@ "safe-buffer": "~5.1.1" } }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-js": { "version": "3.32.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz", @@ -5338,6 +6419,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -5394,6 +6482,34 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -5411,6 +6527,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -5436,6 +6564,16 @@ "node": ">=0.4.0" } }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", @@ -5522,6 +6660,54 @@ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.5.tgz", "integrity": "sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==" }, + "node_modules/dot-prop": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", + "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^4.18.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dot-prop/node_modules/type-fest": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.35.0.tgz", + "integrity": "sha512-2/AwEFQDFEy30iOLjrvHDIH7e4HEWH+f1Yl1bI5XMqzuoCUqwYCdxachgsgv0og/JdVZUhbfjcJAoHj5L1753A==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.4.499", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.499.tgz", @@ -5553,6 +6739,13 @@ "node": ">= 4" } }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true, + "license": "MIT" + }, "node_modules/enhanced-resolve": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", @@ -5706,6 +6899,16 @@ "node": ">= 0.4" } }, + "node_modules/es-escape-html": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/es-escape-html/-/es-escape-html-0.1.1.tgz", + "integrity": "sha512-yUx1o+8RsG7UlszmYPtks+dm6Lho2m8lgHMOsLJQsFI0R8XwUJwiMhM1M4E/S8QLeGyf6MkDV/pWgjQ0tdTSyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.x" + } + }, "node_modules/es-module-lexer": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", @@ -5769,6 +6972,44 @@ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==" }, + "node_modules/esbuild": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.15.tgz", + "integrity": "sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.15", + "@esbuild/android-arm64": "0.17.15", + "@esbuild/android-x64": "0.17.15", + "@esbuild/darwin-arm64": "0.17.15", + "@esbuild/darwin-x64": "0.17.15", + "@esbuild/freebsd-arm64": "0.17.15", + "@esbuild/freebsd-x64": "0.17.15", + "@esbuild/linux-arm": "0.17.15", + "@esbuild/linux-arm64": "0.17.15", + "@esbuild/linux-ia32": "0.17.15", + "@esbuild/linux-loong64": "0.17.15", + "@esbuild/linux-mips64el": "0.17.15", + "@esbuild/linux-ppc64": "0.17.15", + "@esbuild/linux-riscv64": "0.17.15", + "@esbuild/linux-s390x": "0.17.15", + "@esbuild/linux-x64": "0.17.15", + "@esbuild/netbsd-x64": "0.17.15", + "@esbuild/openbsd-x64": "0.17.15", + "@esbuild/sunos-x64": "0.17.15", + "@esbuild/win32-arm64": "0.17.15", + "@esbuild/win32-ia32": "0.17.15", + "@esbuild/win32-x64": "0.17.15" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -6189,6 +7430,19 @@ "node": ">=0.10.0" } }, + "node_modules/eta": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-3.5.0.tgz", + "integrity": "sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "url": "https://github.com/eta-dev/eta?sponsor=1" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -6243,6 +7497,19 @@ "node": ">= 0.8.0" } }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", @@ -6330,8 +7597,7 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -6344,6 +7610,24 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, + "node_modules/fast-xml-parser": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.2.tgz", + "integrity": "sha512-xmnYV9o0StIz/0ArdzmWTxn9oDy0lH8Z80/8X/TD2EUQKXY4DHxoT9mYBqgGIG17DgddCJtH1M6DriMbalNsAA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -6394,6 +7678,17 @@ "node": ">=8" } }, + "node_modules/find-node-modules": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz", + "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "findup-sync": "^4.0.0", + "merge": "^2.1.1" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -6410,6 +7705,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/findup-sync": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", + "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^4.0.2", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -6429,6 +7740,13 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true, + "license": "MIT" + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -6443,6 +7761,50 @@ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==" }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -6665,6 +8027,51 @@ "node": "*" } }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/globals": { "version": "13.17.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", @@ -6778,7 +8185,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -6845,6 +8251,29 @@ "node": ">= 0.4" } }, + "node_modules/header-range-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/header-range-parser/-/header-range-parser-1.1.3.tgz", + "integrity": "sha512-B9zCFt3jH8g09LR1vHL4pcAn8yMEtlSlOUdQemzHMRKMImNIhhszdeosYFfNW0WXKQtXIlWB+O4owHJKvEJYaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -6877,6 +8306,13 @@ "node": ">= 6" } }, + "node_modules/http-status-emojis": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http-status-emojis/-/http-status-emojis-2.2.0.tgz", + "integrity": "sha512-ompKtgwpx8ff0hsbpIB7oE4ax1LXoHmftsHHStMELX56ivG3GhofTX8ZHWlUaFKfGjcGjw6G3rPk7dJRXMmbbg==", + "dev": true, + "license": "MIT" + }, "node_modules/http2-client": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", @@ -6975,6 +8411,16 @@ "node": ">=0.8.19" } }, + "node_modules/inflection": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.2.tgz", + "integrity": "sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -6989,6 +8435,13 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -7012,6 +8465,16 @@ "node": ">= 0.10" } }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, "node_modules/is-alphabetical": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", @@ -7163,6 +8626,21 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -7209,6 +8687,24 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -7383,6 +8879,21 @@ "node": ">=0.10.0" } }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -7470,6 +8981,22 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -7953,7 +9480,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", @@ -7968,7 +9494,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "engines": { "node": ">=10" }, @@ -7980,7 +9505,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -7989,7 +9513,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", @@ -8004,7 +9527,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -8013,7 +9535,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -8026,8 +9547,7 @@ "node_modules/jest-matcher-utils/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/jest-message-util": { "version": "29.7.0", @@ -8530,20 +10050,6 @@ } } }, - "node_modules/jsdom/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/jsdom/node_modules/tr46": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", @@ -8599,6 +10105,15 @@ } } }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -8642,6 +10157,77 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/json-server": { + "version": "1.0.0-beta.3", + "resolved": "https://registry.npmjs.org/json-server/-/json-server-1.0.0-beta.3.tgz", + "integrity": "sha512-DwE69Ep5ccwIJZBUIWEENC30Yj8bwr4Ax9W9VoIWAYnB8Sj4ReptscO8/DRHv/nXwVlmb3Bk73Ls86+VZdYkkA==", + "dev": true, + "license": "SEE LICENSE IN ./LICENSE", + "dependencies": { + "@tinyhttp/app": "^2.4.0", + "@tinyhttp/cors": "^2.0.1", + "@tinyhttp/logger": "^2.0.0", + "chalk": "^5.3.0", + "chokidar": "^4.0.1", + "dot-prop": "^9.0.0", + "eta": "^3.5.0", + "inflection": "^3.0.0", + "json5": "^2.2.3", + "lowdb": "^7.0.1", + "milliparsec": "^4.0.0", + "sirv": "^2.0.4", + "sort-on": "^6.1.0" + }, + "bin": { + "json-server": "lib/bin.js" + }, + "engines": { + "node": ">=18.3" + } + }, + "node_modules/json-server/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/json-server/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/json-server/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -8668,6 +10254,33 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonpath-plus": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz", + "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==", + "license": "MIT", + "dependencies": { + "@jsep-plugin/assignment": "^1.3.0", + "@jsep-plugin/regex": "^1.0.4", + "jsep": "^1.4.0" + }, + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -8686,11 +10299,17 @@ "node": ">=6" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true, + "license": "MIT" + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, "engines": { "node": ">=6" } @@ -8802,6 +10421,22 @@ "loose-envify": "cli.js" } }, + "node_modules/lowdb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz", + "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", + "dev": true, + "license": "MIT", + "dependencies": { + "steno": "^4.0.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -9037,6 +10672,13 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/merge": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", + "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==", + "dev": true, + "license": "MIT" + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -9165,6 +10807,32 @@ "node": ">=8.6" } }, + "node_modules/milliparsec": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/milliparsec/-/milliparsec-4.0.0.tgz", + "integrity": "sha512-/wk9d4Z6/9ZvoEH/6BI4TrTCgmkpZPuSRN/6fI9aUHOfXdNTuj/VhLS7d+NqG26bi6L9YmGXutVYvWC8zQ0qtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -9209,6 +10877,29 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mobx": { "version": "6.12.3", "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.12.3.tgz", @@ -9275,6 +10966,16 @@ "node": ">=4" } }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -9303,6 +11004,16 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -9352,17 +11063,97 @@ "es6-promise": "^3.2.1" } }, - "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-dts": { + "version": "1.3.12", + "resolved": "https://registry.npmjs.org/npm-dts/-/npm-dts-1.3.12.tgz", + "integrity": "sha512-3pFsz7Gf1u0cyQE2czXt8Y0hKe6kczHxlFbVrr74xWweNUit2tCDbOcL4n6KaWxyimGNJ4gzOa8KkShFA8hrdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "args": "5.0.3", + "find-node-modules": "2.1.3", + "mkdirp": "1.0.4", + "npm-run": "5.0.1", + "rimraf": "3.0.2", + "tmp": "0.2.1", + "winston": "3.7.2" + }, + "bin": { + "npm-dts": "cli.js" + } + }, + "node_modules/npm-dts/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/npm-path": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.4.tgz", + "integrity": "sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "which": "^1.2.10" + }, + "bin": { + "npm-path": "bin/npm-path" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm-path/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/npm-run": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npm-run/-/npm-run-5.0.1.tgz", + "integrity": "sha512-s7FyRpHUgaJfzkRgOnevX8rAWWsv1dofY1XS7hliWCF6LSQh+HtDfBvpigPS1krLvXw+Fi17CYMY8mUtblnyWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0", + "npm-path": "^2.0.4", + "npm-which": "^3.0.1", + "serializerr": "^1.0.3" + }, + "bin": { + "npm-run": "bin/npm-run.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4.2.0" } }, "node_modules/npm-run-path": { @@ -9377,6 +11168,37 @@ "node": ">=8" } }, + "node_modules/npm-which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz", + "integrity": "sha512-CM8vMpeFQ7MAPin0U3wzDhSGV0hMHNwHU0wjo402IVizPDrs45jSfSuoC+wThevY88LQti8VvaAnqYAeVy3I1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^2.9.0", + "npm-path": "^2.0.2", + "which": "^1.2.10" + }, + "bin": { + "npm-which": "bin/npm-which.js" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/npm-which/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/null-loader": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz", @@ -9569,6 +11391,16 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -9584,12 +11416,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/openapi-sampler": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.5.1.tgz", - "integrity": "sha512-tIWIrZUKNAsbqf3bd9U1oH6JEXo8LNYuDlXw26By67EygpjT+ArFnsxxyTMjFWRfbqo5ozkvgSQDK69Gd8CddA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.6.1.tgz", + "integrity": "sha512-s1cIatOqrrhSj2tmJ4abFYZQK6l5v+V4toO5q1Pa0DyN8mtyqy2I+Qrj5W9vOELEtybIMQs/TBZGVO/DtTFK8w==", + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.7", + "fast-xml-parser": "^4.5.0", "json-pointer": "0.6.2" } }, @@ -9685,6 +11537,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/package-manager-detector": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.7.tgz", @@ -9739,6 +11598,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -9787,6 +11656,30 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -9796,6 +11689,19 @@ "node": ">=8" } }, + "node_modules/pegjs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", + "integrity": "sha512-qI5+oFNEGi3L5HAxDwN2LA4Gg7irF70Zs25edhjld9QemOgp0CbvMtbFcMvFtEo1OityPrcCzkQFB8JP/hxgow==", + "dev": true, + "license": "MIT", + "bin": { + "pegjs": "bin/pegjs" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/perfect-scrollbar": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz", @@ -10104,7 +12010,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, "engines": { "node": ">=6" } @@ -10246,9 +12151,10 @@ } }, "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -10376,6 +12282,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexparam": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-2.0.2.tgz", + "integrity": "sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -10531,6 +12447,20 @@ "node": ">=8" } }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -10574,6 +12504,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -10643,6 +12585,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -10710,6 +12662,22 @@ "randombytes": "^2.1.0" } }, + "node_modules/serializerr": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/serializerr/-/serializerr-1.0.3.tgz", + "integrity": "sha512-yXUlHj0fjbndhACj2XWtIH5eJv7b/uadyl7CJA8b9wL5mIKm+g0/sL7rDzEmjC+k5y8ggcaP8i049F4FxA0U9Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "protochain": "^1.0.5" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -10852,6 +12820,23 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true, + "license": "MIT" + }, "node_modules/simple-websocket": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/simple-websocket/-/simple-websocket-9.1.0.tgz", @@ -10878,6 +12863,21 @@ "ws": "^7.4.2" } }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -10916,6 +12916,22 @@ "node": ">=8.0.0" } }, + "node_modules/sort-on": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sort-on/-/sort-on-6.1.0.tgz", + "integrity": "sha512-WTECP0nYNWO1n2g5bpsV0yZN9cBmZsF8ThHFbOqVN0HBFRoaQZLLEMvMmJlKHNPYQeVngeI5+jJzIfFqOIo1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-prop": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -10970,6 +12986,16 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -10991,6 +13017,19 @@ "node": ">=8" } }, + "node_modules/steno": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz", + "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/stickyfill": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stickyfill/-/stickyfill-1.1.1.tgz", @@ -11030,6 +13069,22 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -11090,6 +13145,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -11120,6 +13189,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, "node_modules/styled-components": { "version": "6.0.7", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.0.7.tgz", @@ -11177,7 +13252,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -11366,6 +13440,13 @@ "node": "*" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true, + "license": "MIT" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -11409,6 +13490,16 @@ "node": ">=8.0" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tough-cookie": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", @@ -11429,6 +13520,16 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/trough": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", @@ -11694,6 +13795,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/typescript": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", @@ -11734,6 +13841,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", + "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -11932,7 +14048,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -12338,6 +14453,43 @@ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, + "node_modules/winston": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz", + "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -12368,6 +14520,25 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -12518,6 +14689,7 @@ "chokidar": "^3.5.1", "colorette": "^1.2.0", "core-js": "^3.32.1", + "dotenv": "^16.4.7", "form-data": "^4.0.0", "get-port-please": "^3.0.1", "glob": "^7.1.6", @@ -12551,19 +14723,6 @@ "npm": ">=9.5.0" } }, - "packages/cli/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "packages/core": { "name": "@redocly/openapi-core", "version": "1.29.0", @@ -12613,6 +14772,281 @@ "engines": { "node": ">= 14" } + }, + "packages/respect-core": { + "name": "@redocly/respect-core", + "version": "1.29.0", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "@faker-js/faker": "^7.6.0", + "@jest/expect-utils": "^29.3.1", + "@redocly/ajv": "8.11.2", + "@redocly/openapi-core": "1.29.0", + "better-ajv-errors": "^1.2.0", + "colorette": "^2.0.20", + "concat-stream": "^2.0.0", + "cookie": "^0.7.2", + "dotenv": "16.4.5", + "form-data": "4.0.0", + "jest-diff": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "js-yaml": "4.1.0", + "json-pointer": "^0.6.2", + "jsonpath-plus": "^10.0.6", + "open": "^10.1.0", + "openapi-sampler": "^1.6.1", + "outdent": "^0.8.0", + "set-cookie-parser": "^2.3.5", + "undici": "^6.21.1", + "yargs": "^17.6.2" + }, + "devDependencies": { + "@types/concat-stream": "^2.0.3", + "@types/cookie": "0.6.0", + "@types/har-format": "^1.2.16", + "@types/jest": "^29.2.4", + "@types/js-yaml": "4.0.9", + "@types/json-pointer": "1.0.34", + "@types/node": "22.10.5", + "@types/set-cookie-parser": "2.4.10", + "@types/yargs": "17.0.32", + "esbuild": "0.17.15", + "jest": "^29.3.1", + "jest-snapshot": "^29.3.1", + "json-schema-to-ts": "^3.0.1", + "npm-dts": "1.3.12", + "rimraf": "5.0.7", + "ts-jest": "^29.0.3", + "typescript": "5.6.2" + } + }, + "packages/respect-core/node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "packages/respect-core/node_modules/@types/node": { + "version": "22.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", + "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "packages/respect-core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "packages/respect-core/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/respect-core/node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, + "packages/respect-core/node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/respect-core/node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "packages/respect-core/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/respect-core/node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/respect-core/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/respect-core/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/respect-core/node_modules/outdent": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.8.0.tgz", + "integrity": "sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==", + "license": "MIT" + }, + "packages/respect-core/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/respect-core/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "packages/respect-core/node_modules/rimraf": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", + "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/respect-core/node_modules/typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "packages/respect-core/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, + "packages/respect-core/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "packages/respect-core/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } } }, "dependencies": { @@ -14273,29 +16707,194 @@ "prettier": "^2.7.1" } }, - "@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true + "@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "requires": { + "@emotion/memoize": "^0.8.1" + } + }, + "@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "@esbuild/android-arm": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.15.tgz", + "integrity": "sha512-sRSOVlLawAktpMvDyJIkdLI/c/kdRTOqo8t6ImVxg8yT7LQDUYV5Rp2FKeEosLr6ZCja9UjYAzyRSxGteSJPYg==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.15.tgz", + "integrity": "sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.15.tgz", + "integrity": "sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.15.tgz", + "integrity": "sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.15.tgz", + "integrity": "sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.15.tgz", + "integrity": "sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.15.tgz", + "integrity": "sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.15.tgz", + "integrity": "sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.15.tgz", + "integrity": "sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.15.tgz", + "integrity": "sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.15.tgz", + "integrity": "sha512-k7FsUJjGGSxwnBmMh8d7IbObWu+sF/qbwc+xKZkBe/lTAF16RqxRCnNHA7QTd3oS2AfGBAnHlXL67shV5bBThQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.15.tgz", + "integrity": "sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.15.tgz", + "integrity": "sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.15.tgz", + "integrity": "sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.15.tgz", + "integrity": "sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.15.tgz", + "integrity": "sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.15.tgz", + "integrity": "sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.15.tgz", + "integrity": "sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w==", + "dev": true, + "optional": true }, - "@emotion/is-prop-valid": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", - "requires": { - "@emotion/memoize": "^0.8.1" - } + "@esbuild/sunos-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.15.tgz", + "integrity": "sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ==", + "dev": true, + "optional": true }, - "@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + "@esbuild/win32-arm64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.15.tgz", + "integrity": "sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q==", + "dev": true, + "optional": true }, - "@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + "@esbuild/win32-ia32": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.15.tgz", + "integrity": "sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.15.tgz", + "integrity": "sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA==", + "dev": true, + "optional": true }, "@eslint-community/eslint-utils": { "version": "4.4.0", @@ -14355,6 +16954,11 @@ "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.2.4.tgz", "integrity": "sha512-DNmuVYeOqFmLmJIJvFIX1TKttOZVI9FwDrqDujhyArjqtXUaZuuB+SuDBTQq3Ev368a7ONJiAJ8m9zi0+IBqZQ==" }, + "@faker-js/faker": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-7.6.0.tgz", + "integrity": "sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==" + }, "@humanwhocodes/config-array": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", @@ -14393,12 +16997,82 @@ "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", "dev": true }, + "@humanwhocodes/momoa": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-2.0.4.tgz", + "integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==" + }, "@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -14583,7 +17257,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, "requires": { "jest-get-type": "^29.6.3" }, @@ -14591,8 +17264,7 @@ "jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==" } } }, @@ -14658,7 +17330,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, "requires": { "@sinclair/typebox": "^0.27.8" } @@ -14799,6 +17470,18 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "requires": {} + }, + "@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "requires": {} + }, "@manypkg/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", @@ -15078,6 +17761,12 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==" }, + "@polka/url": { + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "dev": true + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -15163,6 +17852,7 @@ "chokidar": "^3.5.1", "colorette": "^1.2.0", "core-js": "^3.32.1", + "dotenv": "^16.4.7", "form-data": "^4.0.0", "get-port-please": "^3.0.1", "glob": "^7.1.6", @@ -15177,18 +17867,6 @@ "styled-components": "^6.0.7", "typescript": "5.5.3", "yargs": "17.0.1" - }, - "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } } }, "@redocly/config": { @@ -15232,11 +17910,203 @@ } } }, + "@redocly/respect-core": { + "version": "file:packages/respect-core", + "requires": { + "@faker-js/faker": "^7.6.0", + "@jest/expect-utils": "^29.3.1", + "@redocly/ajv": "8.11.2", + "@redocly/openapi-core": "1.29.0", + "@types/concat-stream": "^2.0.3", + "@types/cookie": "0.6.0", + "@types/har-format": "^1.2.16", + "@types/jest": "^29.2.4", + "@types/js-yaml": "4.0.9", + "@types/json-pointer": "1.0.34", + "@types/node": "22.10.5", + "@types/set-cookie-parser": "2.4.10", + "@types/yargs": "17.0.32", + "better-ajv-errors": "^1.2.0", + "colorette": "^2.0.20", + "concat-stream": "^2.0.0", + "cookie": "^0.7.2", + "dotenv": "16.4.5", + "esbuild": "0.17.15", + "form-data": "4.0.0", + "jest": "^29.3.1", + "jest-diff": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-snapshot": "^29.3.1", + "js-yaml": "4.1.0", + "json-pointer": "^0.6.2", + "json-schema-to-ts": "^3.0.1", + "jsonpath-plus": "^10.0.6", + "npm-dts": "1.3.12", + "open": "^10.1.0", + "openapi-sampler": "^1.6.1", + "outdent": "^0.8.0", + "rimraf": "5.0.7", + "set-cookie-parser": "^2.3.5", + "ts-jest": "^29.0.3", + "typescript": "5.6.2", + "undici": "^6.21.1", + "yargs": "^17.6.2" + }, + "dependencies": { + "@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "requires": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "@types/node": { + "version": "22.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", + "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", + "dev": true, + "requires": { + "undici-types": "~6.20.0" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==" + }, + "dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" + }, + "glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==" + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "outdent": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.8.0.tgz", + "integrity": "sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==" + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "rimraf": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", + "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", + "dev": true, + "requires": { + "glob": "^10.3.7" + } + }, + "typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "dev": true + }, + "undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + } + } + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" }, "@sinonjs/commons": { "version": "3.0.0", @@ -15244,18 +18114,191 @@ "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "requires": { - "type-detect": "4.0.8" + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@tinyhttp/accepts": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/accepts/-/accepts-2.2.3.tgz", + "integrity": "sha512-9pQN6pJAJOU3McmdJWTcyq7LLFW8Lj5q+DadyKcvp+sxMkEpktKX5sbfJgJuOvjk6+1xWl7pe0YL1US1vaO/1w==", + "dev": true, + "requires": { + "mime": "4.0.4", + "negotiator": "^0.6.3" + } + }, + "@tinyhttp/app": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tinyhttp/app/-/app-2.5.0.tgz", + "integrity": "sha512-mwVY6RhTqF/s49tqNqpkRWbXAC7OmfpxR6YI6atp9xDauUOv3n1hv38AKy89Ga8HC8G2p1KR9vPD66VmF6VMnw==", + "dev": true, + "requires": { + "@tinyhttp/cookie": "2.1.1", + "@tinyhttp/proxy-addr": "2.2.0", + "@tinyhttp/req": "2.2.4", + "@tinyhttp/res": "2.2.4", + "@tinyhttp/router": "2.2.3", + "header-range-parser": "1.1.3", + "regexparam": "^2.0.2" + } + }, + "@tinyhttp/content-disposition": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-disposition/-/content-disposition-2.2.2.tgz", + "integrity": "sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g==", + "dev": true + }, + "@tinyhttp/content-type": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-type/-/content-type-0.1.4.tgz", + "integrity": "sha512-dl6f3SHIJPYbhsW1oXdrqOmLSQF/Ctlv3JnNfXAE22kIP7FosqJHxkz/qj2gv465prG8ODKH5KEyhBkvwrueKQ==", + "dev": true + }, + "@tinyhttp/cookie": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cookie/-/cookie-2.1.1.tgz", + "integrity": "sha512-h/kL9jY0e0Dvad+/QU3efKZww0aTvZJslaHj3JTPmIPC9Oan9+kYqmh3M6L5JUQRuTJYFK2nzgL2iJtH2S+6dA==", + "dev": true + }, + "@tinyhttp/cookie-signature": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cookie-signature/-/cookie-signature-2.1.1.tgz", + "integrity": "sha512-VDsSMY5OJfQJIAtUgeQYhqMPSZptehFSfvEEtxr+4nldPA8IImlp3QVcOVuK985g4AFR4Hl1sCbWCXoqBnVWnw==", + "dev": true + }, + "@tinyhttp/cors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cors/-/cors-2.0.1.tgz", + "integrity": "sha512-qrmo6WJuaiCzKWagv2yA/kw6hIISfF/hOqPWwmI6w0o8apeTMmRN3DoCFvQ/wNVuWVdU5J4KU7OX8aaSOEq51A==", + "dev": true, + "requires": { + "@tinyhttp/vary": "^0.1.3" + } + }, + "@tinyhttp/encode-url": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/encode-url/-/encode-url-2.1.1.tgz", + "integrity": "sha512-AhY+JqdZ56qV77tzrBm0qThXORbsVjs/IOPgGCS7x/wWnsa/Bx30zDUU/jPAUcSzNOzt860x9fhdGpzdqbUeUw==", + "dev": true + }, + "@tinyhttp/etag": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/etag/-/etag-2.1.2.tgz", + "integrity": "sha512-j80fPKimGqdmMh6962y+BtQsnYPVCzZfJw0HXjyH70VaJBHLKGF+iYhcKqzI3yef6QBNa8DKIPsbEYpuwApXTw==", + "dev": true + }, + "@tinyhttp/forwarded": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/forwarded/-/forwarded-2.1.1.tgz", + "integrity": "sha512-nO3kq0R1LRl2+CAMlnggm22zE6sT8gfvGbNvSitV6F9eaUSurHP0A8YZFMihSkugHxK+uIegh1TKrqgD8+lyGQ==", + "dev": true + }, + "@tinyhttp/logger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tinyhttp/logger/-/logger-2.0.0.tgz", + "integrity": "sha512-8DfLQjGDIaIJeivYamVrrpmwmsGwS8wt2DGvzlcY5HEBagdiI4QJy/veAFcUHuaJqufn4wLwmn4q5VUkW8BCpQ==", + "dev": true, + "requires": { + "colorette": "^2.0.20", + "dayjs": "^1.11.10", + "http-status-emojis": "^2.2.0" + }, + "dependencies": { + "colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + } + } + }, + "@tinyhttp/proxy-addr": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@tinyhttp/proxy-addr/-/proxy-addr-2.2.0.tgz", + "integrity": "sha512-WM/PPL9xNvrs7/8Om5nhKbke5FHrP3EfjOOR+wBnjgESfibqn0K7wdUTnzSLp1lBmemr88os1XvzwymSgaibyA==", + "dev": true, + "requires": { + "@tinyhttp/forwarded": "2.1.1", + "ipaddr.js": "^2.2.0" + } + }, + "@tinyhttp/req": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/req/-/req-2.2.4.tgz", + "integrity": "sha512-lQAZIAo0NOeghxFOZS57tQzxpHSPPLs9T68Krq2BncEBImKwqaDKUt7M9Y5Kb+rvC/GwIL3LeErhkg7f5iG4IQ==", + "dev": true, + "requires": { + "@tinyhttp/accepts": "2.2.3", + "@tinyhttp/type-is": "2.2.4", + "@tinyhttp/url": "2.1.1", + "header-range-parser": "^1.1.3" + } + }, + "@tinyhttp/res": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/res/-/res-2.2.4.tgz", + "integrity": "sha512-ETBRShnO19oJyIg2XQHQoofXPWeTXPAuwnIVYkU8WaftvXd/Vz4y5+WFQDHUzKlmdGOw5fAFnrEU7pIVMeFeVA==", + "dev": true, + "requires": { + "@tinyhttp/content-disposition": "2.2.2", + "@tinyhttp/cookie": "2.1.1", + "@tinyhttp/cookie-signature": "2.1.1", + "@tinyhttp/encode-url": "2.1.1", + "@tinyhttp/req": "2.2.4", + "@tinyhttp/send": "2.2.3", + "@tinyhttp/vary": "^0.1.3", + "es-escape-html": "^0.1.1", + "mime": "4.0.4" + } + }, + "@tinyhttp/router": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/router/-/router-2.2.3.tgz", + "integrity": "sha512-O0MQqWV3Vpg/uXsMYg19XsIgOhwjyhTYWh51Qng7bxqXixxx2PEvZWnFjP7c84K7kU/nUX41KpkEBTLnznk9/Q==", + "dev": true + }, + "@tinyhttp/send": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/send/-/send-2.2.3.tgz", + "integrity": "sha512-o4cVHHGQ8WjVBS8UT0EE/2WnjoybrfXikHwsRoNlG1pfrC/Sd01u1N4Te8cOd/9aNGLr4mGxWb5qTm2RRtEi7g==", + "dev": true, + "requires": { + "@tinyhttp/content-type": "^0.1.4", + "@tinyhttp/etag": "2.1.2", + "mime": "4.0.4" } }, - "@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "@tinyhttp/type-is": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/type-is/-/type-is-2.2.4.tgz", + "integrity": "sha512-7F328NheridwjIfefBB2j1PEcKKABpADgv7aCJaE8x8EON77ZFrAkI3Rir7pGjopV7V9MBmW88xUQigBEX2rmQ==", "dev": true, "requires": { - "@sinonjs/commons": "^3.0.0" + "@tinyhttp/content-type": "^0.1.4", + "mime": "4.0.4" } }, + "@tinyhttp/url": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/url/-/url-2.1.1.tgz", + "integrity": "sha512-POJeq2GQ5jI7Zrdmj22JqOijB5/GeX+LEX7DUdml1hUnGbJOTWDx7zf2b5cCERj7RoXL67zTgyzVblBJC+NJWg==", + "dev": true + }, + "@tinyhttp/vary": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/vary/-/vary-0.1.3.tgz", + "integrity": "sha512-SoL83sQXAGiHN1jm2VwLUWQSQeDAAl1ywOm6T0b0Cg1CZhVsjoiZadmjhxF6FHCCY7OHHVaLnTgSMxTPIDLxMg==", + "dev": true + }, "@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -15303,12 +18346,27 @@ "@babel/types": "^7.20.7" } }, + "@types/concat-stream": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-2.0.3.tgz", + "integrity": "sha512-3qe4oQAPNwVNwK4C9c8u+VJqv9kez+2MR4qJpoPFfXtgxxif1QbFusvXzK0/Wra2VX07smostI2VMmJNSpZjuQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/configstore": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/configstore/-/configstore-5.0.1.tgz", "integrity": "sha512-c/QCznvk7bLKGhHETj29rqKufui3jaAxjBhK4R2zUrMG5UG0qTwfWYxBoUbH8JCyDjdCWMIxPJ7/Fdz1UcAnWg==", "dev": true }, + "@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true + }, "@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -15342,6 +18400,12 @@ "@types/node": "*" } }, + "@types/har-format": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz", + "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==", + "dev": true + }, "@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -15392,9 +18456,9 @@ "dev": true }, "@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", "dev": true }, "@types/jsdom": { @@ -15408,6 +18472,12 @@ "parse5": "^7.0.0" } }, + "@types/json-pointer": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/@types/json-pointer/-/json-pointer-1.0.34.tgz", + "integrity": "sha512-JRnWcxzXSaLei98xgw1B7vAeBVOrkyw0+Rt9j1QoJrczE78OpHsyQC8GNbuhw+/2vxxDe58QvWnngS86CoIbRg==", + "dev": true + }, "@types/json-schema": { "version": "7.0.14", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", @@ -15501,6 +18571,15 @@ "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, + "@types/set-cookie-parser": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz", + "integrity": "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/sizzle": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", @@ -15889,7 +18968,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -15900,8 +18978,7 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" } } }, @@ -15968,6 +19045,94 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "args": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", + "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", + "dev": true, + "requires": { + "camelcase": "5.0.0", + "chalk": "2.4.2", + "leven": "2.1.0", + "mri": "1.1.4" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", + "dev": true + }, + "mri": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", + "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -16052,6 +19217,12 @@ "is-shared-array-buffer": "^1.0.2" } }, + "async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -16202,6 +19373,18 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "better-ajv-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/better-ajv-errors/-/better-ajv-errors-1.2.0.tgz", + "integrity": "sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==", + "requires": { + "@babel/code-frame": "^7.16.0", + "@humanwhocodes/momoa": "^2.0.2", + "chalk": "^4.1.2", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0 < 4" + } + }, "better-path-resolve": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", @@ -16270,8 +19453,15 @@ "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "requires": { + "run-applescript": "^7.0.0" + } }, "call-bind": { "version": "1.0.7", @@ -16323,7 +19513,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -16445,6 +19634,33 @@ "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + }, + "dependencies": { + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + } + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -16458,11 +19674,31 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" }, + "colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "requires": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -16482,6 +19718,17 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "convert-source-map": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", @@ -16490,6 +19737,11 @@ "safe-buffer": "~5.1.1" } }, + "cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" + }, "core-js": { "version": "3.32.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz", @@ -16655,6 +19907,12 @@ "is-data-view": "^1.0.1" } }, + "dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -16693,6 +19951,20 @@ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true }, + "default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "requires": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + } + }, + "default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==" + }, "define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -16704,6 +19976,11 @@ "gopd": "^1.0.1" } }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==" + }, "define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -16720,6 +19997,12 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "dev": true + }, "detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", @@ -16784,6 +20067,34 @@ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.5.tgz", "integrity": "sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==" }, + "dot-prop": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", + "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", + "dev": true, + "requires": { + "type-fest": "^4.18.2" + }, + "dependencies": { + "type-fest": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.35.0.tgz", + "integrity": "sha512-2/AwEFQDFEy30iOLjrvHDIH7e4HEWH+f1Yl1bI5XMqzuoCUqwYCdxachgsgv0og/JdVZUhbfjcJAoHj5L1753A==", + "dev": true + } + } + }, + "dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==" + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "electron-to-chromium": { "version": "1.4.499", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.499.tgz", @@ -16806,6 +20117,12 @@ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, "enhanced-resolve": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", @@ -16926,6 +20243,12 @@ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true }, + "es-escape-html": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/es-escape-html/-/es-escape-html-0.1.1.tgz", + "integrity": "sha512-yUx1o+8RsG7UlszmYPtks+dm6Lho2m8lgHMOsLJQsFI0R8XwUJwiMhM1M4E/S8QLeGyf6MkDV/pWgjQ0tdTSyQ==", + "dev": true + }, "es-module-lexer": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", @@ -16977,6 +20300,36 @@ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==" }, + "esbuild": { + "version": "0.17.15", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.15.tgz", + "integrity": "sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw==", + "dev": true, + "requires": { + "@esbuild/android-arm": "0.17.15", + "@esbuild/android-arm64": "0.17.15", + "@esbuild/android-x64": "0.17.15", + "@esbuild/darwin-arm64": "0.17.15", + "@esbuild/darwin-x64": "0.17.15", + "@esbuild/freebsd-arm64": "0.17.15", + "@esbuild/freebsd-x64": "0.17.15", + "@esbuild/linux-arm": "0.17.15", + "@esbuild/linux-arm64": "0.17.15", + "@esbuild/linux-ia32": "0.17.15", + "@esbuild/linux-loong64": "0.17.15", + "@esbuild/linux-mips64el": "0.17.15", + "@esbuild/linux-ppc64": "0.17.15", + "@esbuild/linux-riscv64": "0.17.15", + "@esbuild/linux-s390x": "0.17.15", + "@esbuild/linux-x64": "0.17.15", + "@esbuild/netbsd-x64": "0.17.15", + "@esbuild/openbsd-x64": "0.17.15", + "@esbuild/sunos-x64": "0.17.15", + "@esbuild/win32-arm64": "0.17.15", + "@esbuild/win32-ia32": "0.17.15", + "@esbuild/win32-x64": "0.17.15" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -17299,6 +20652,12 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, + "eta": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-3.5.0.tgz", + "integrity": "sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug==", + "dev": true + }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -17338,6 +20697,15 @@ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, "expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", @@ -17414,8 +20782,7 @@ "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "fast-levenshtein": { "version": "2.0.6", @@ -17428,6 +20795,14 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, + "fast-xml-parser": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.2.tgz", + "integrity": "sha512-xmnYV9o0StIz/0ArdzmWTxn9oDy0lH8Z80/8X/TD2EUQKXY4DHxoT9mYBqgGIG17DgddCJtH1M6DriMbalNsAA==", + "requires": { + "strnum": "^1.0.5" + } + }, "fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -17469,6 +20844,16 @@ "to-regex-range": "^5.0.1" } }, + "find-node-modules": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz", + "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==", + "dev": true, + "requires": { + "findup-sync": "^4.0.0", + "merge": "^2.1.1" + } + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -17479,6 +20864,18 @@ "path-exists": "^4.0.0" } }, + "findup-sync": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", + "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^4.0.2", + "resolve-dir": "^1.0.1" + } + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -17495,6 +20892,12 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -17509,6 +20912,34 @@ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==" }, + "foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -17671,6 +21102,41 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "globals": { "version": "13.17.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", @@ -17751,8 +21217,7 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "has-property-descriptors": { "version": "1.0.2", @@ -17789,7 +21254,22 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "requires": { - "function-bind": "^1.1.2" + "function-bind": "^1.1.2" + } + }, + "header-range-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/header-range-parser/-/header-range-parser-1.1.3.tgz", + "integrity": "sha512-B9zCFt3jH8g09LR1vHL4pcAn8yMEtlSlOUdQemzHMRKMImNIhhszdeosYFfNW0WXKQtXIlWB+O4owHJKvEJYaA==", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" } }, "html-encoding-sniffer": { @@ -17818,6 +21298,12 @@ "debug": "4" } }, + "http-status-emojis": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http-status-emojis/-/http-status-emojis-2.2.0.tgz", + "integrity": "sha512-ompKtgwpx8ff0hsbpIB7oE4ax1LXoHmftsHHStMELX56ivG3GhofTX8ZHWlUaFKfGjcGjw6G3rPk7dJRXMmbbg==", + "dev": true + }, "http2-client": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", @@ -17886,6 +21372,12 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, + "inflection": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.2.tgz", + "integrity": "sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -17900,6 +21392,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -17917,6 +21415,12 @@ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true }, + "ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true + }, "is-alphabetical": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", @@ -18014,6 +21518,11 @@ "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", "dev": true }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==" + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -18044,6 +21553,14 @@ "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", "dev": true }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "requires": { + "is-docker": "^3.0.0" + } + }, "is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -18155,6 +21672,14 @@ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, + "is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "requires": { + "is-inside-container": "^1.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -18224,6 +21749,16 @@ "istanbul-lib-report": "^3.0.0" } }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -18580,7 +22115,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, "requires": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", @@ -18591,20 +22125,17 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" }, "diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==" }, "jest-diff": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, "requires": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", @@ -18615,14 +22146,12 @@ "jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==" }, "pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "requires": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -18632,8 +22161,7 @@ "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" } } }, @@ -19034,17 +22562,6 @@ "xml-name-validator": "^4.0.0" }, "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "tr46": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", @@ -19079,6 +22596,11 @@ } } }, + "jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==" + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -19113,6 +22635,50 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "json-server": { + "version": "1.0.0-beta.3", + "resolved": "https://registry.npmjs.org/json-server/-/json-server-1.0.0-beta.3.tgz", + "integrity": "sha512-DwE69Ep5ccwIJZBUIWEENC30Yj8bwr4Ax9W9VoIWAYnB8Sj4ReptscO8/DRHv/nXwVlmb3Bk73Ls86+VZdYkkA==", + "dev": true, + "requires": { + "@tinyhttp/app": "^2.4.0", + "@tinyhttp/cors": "^2.0.1", + "@tinyhttp/logger": "^2.0.0", + "chalk": "^5.3.0", + "chokidar": "^4.0.1", + "dot-prop": "^9.0.0", + "eta": "^3.5.0", + "inflection": "^3.0.0", + "json5": "^2.2.3", + "lowdb": "^7.0.1", + "milliparsec": "^4.0.0", + "sirv": "^2.0.4", + "sort-on": "^6.1.0" + }, + "dependencies": { + "chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true + }, + "chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "requires": { + "readdirp": "^4.0.1" + } + }, + "readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true + } + } + }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -19133,6 +22699,21 @@ "graceful-fs": "^4.1.6" } }, + "jsonpath-plus": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz", + "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==", + "requires": { + "@jsep-plugin/assignment": "^1.3.0", + "@jsep-plugin/regex": "^1.0.4", + "jsep": "^1.4.0" + } + }, + "jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -19145,11 +22726,16 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" }, "levn": { "version": "0.4.1", @@ -19235,6 +22821,15 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lowdb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz", + "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", + "dev": true, + "requires": { + "steno": "^4.0.2" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -19414,6 +23009,12 @@ } } }, + "merge": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", + "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==", + "dev": true + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -19502,6 +23103,18 @@ "picomatch": "^2.3.1" } }, + "milliparsec": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/milliparsec/-/milliparsec-4.0.0.tgz", + "integrity": "sha512-/wk9d4Z6/9ZvoEH/6BI4TrTCgmkpZPuSRN/6fI9aUHOfXdNTuj/VhLS7d+NqG26bi6L9YmGXutVYvWC8zQ0qtA==", + "dev": true + }, + "mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -19534,6 +23147,18 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, "mobx": { "version": "6.12.3", "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.12.3.tgz", @@ -19561,6 +23186,12 @@ "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "dev": true }, + "mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -19577,6 +23208,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true + }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -19622,6 +23259,64 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, + "npm-dts": { + "version": "1.3.12", + "resolved": "https://registry.npmjs.org/npm-dts/-/npm-dts-1.3.12.tgz", + "integrity": "sha512-3pFsz7Gf1u0cyQE2czXt8Y0hKe6kczHxlFbVrr74xWweNUit2tCDbOcL4n6KaWxyimGNJ4gzOa8KkShFA8hrdA==", + "dev": true, + "requires": { + "args": "5.0.3", + "find-node-modules": "2.1.3", + "mkdirp": "1.0.4", + "npm-run": "5.0.1", + "rimraf": "3.0.2", + "tmp": "0.2.1", + "winston": "3.7.2" + }, + "dependencies": { + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + } + } + }, + "npm-path": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.4.tgz", + "integrity": "sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==", + "dev": true, + "requires": { + "which": "^1.2.10" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "npm-run": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npm-run/-/npm-run-5.0.1.tgz", + "integrity": "sha512-s7FyRpHUgaJfzkRgOnevX8rAWWsv1dofY1XS7hliWCF6LSQh+HtDfBvpigPS1krLvXw+Fi17CYMY8mUtblnyWw==", + "dev": true, + "requires": { + "minimist": "^1.2.0", + "npm-path": "^2.0.4", + "npm-which": "^3.0.1", + "serializerr": "^1.0.3" + } + }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -19631,6 +23326,28 @@ "path-key": "^3.0.0" } }, + "npm-which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz", + "integrity": "sha512-CM8vMpeFQ7MAPin0U3wzDhSGV0hMHNwHU0wjo402IVizPDrs45jSfSuoC+wThevY88LQti8VvaAnqYAeVy3I1A==", + "dev": true, + "requires": { + "commander": "^2.9.0", + "npm-path": "^2.0.2", + "which": "^1.2.10" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "null-loader": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz", @@ -19768,6 +23485,15 @@ "wrappy": "1" } }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "requires": { + "fn.name": "1.x.x" + } + }, "onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -19777,12 +23503,24 @@ "mimic-fn": "^2.1.0" } }, + "open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "requires": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + } + }, "openapi-sampler": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.5.1.tgz", - "integrity": "sha512-tIWIrZUKNAsbqf3bd9U1oH6JEXo8LNYuDlXw26By67EygpjT+ArFnsxxyTMjFWRfbqo5ozkvgSQDK69Gd8CddA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.6.1.tgz", + "integrity": "sha512-s1cIatOqrrhSj2tmJ4abFYZQK6l5v+V4toO5q1Pa0DyN8mtyqy2I+Qrj5W9vOELEtybIMQs/TBZGVO/DtTFK8w==", "requires": { "@types/json-schema": "^7.0.7", + "fast-xml-parser": "^4.5.0", "json-pointer": "0.6.2" } }, @@ -19851,6 +23589,12 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "package-manager-detector": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.7.tgz", @@ -19892,6 +23636,12 @@ "lines-and-columns": "^1.1.6" } }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true + }, "parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -19928,12 +23678,36 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + } + } + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pegjs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", + "integrity": "sha512-qI5+oFNEGi3L5HAxDwN2LA4Gg7irF70Zs25edhjld9QemOgp0CbvMtbFcMvFtEo1OityPrcCzkQFB8JP/hxgow==", + "dev": true + }, "perfect-scrollbar": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz", @@ -20162,8 +23936,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "pure-rand": { "version": "6.0.4", @@ -20262,9 +24035,9 @@ } }, "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -20359,6 +24132,12 @@ "set-function-name": "^2.0.1" } }, + "regexparam": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-2.0.2.tgz", + "integrity": "sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==", + "dev": true + }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -20470,6 +24249,16 @@ } } }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -20497,6 +24286,11 @@ "glob": "^7.1.3" } }, + "run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==" + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -20542,6 +24336,12 @@ "is-regex": "^1.1.4" } }, + "safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -20593,6 +24393,20 @@ "randombytes": "^2.1.0" } }, + "serializerr": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/serializerr/-/serializerr-1.0.3.tgz", + "integrity": "sha512-yXUlHj0fjbndhACj2XWtIH5eJv7b/uadyl7CJA8b9wL5mIKm+g0/sL7rDzEmjC+k5y8ggcaP8i049F4FxA0U9Q==", + "dev": true, + "requires": { + "protochain": "^1.0.5" + } + }, + "set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==" + }, "set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -20714,6 +24528,23 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, "simple-websocket": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/simple-websocket/-/simple-websocket-9.1.0.tgz", @@ -20726,6 +24557,17 @@ "ws": "^7.4.2" } }, + "sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "requires": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -20758,6 +24600,15 @@ "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.7.tgz", "integrity": "sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg==" }, + "sort-on": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sort-on/-/sort-on-6.1.0.tgz", + "integrity": "sha512-WTECP0nYNWO1n2g5bpsV0yZN9cBmZsF8ThHFbOqVN0HBFRoaQZLLEMvMmJlKHNPYQeVngeI5+jJzIfFqOIo1OA==", + "dev": true, + "requires": { + "dot-prop": "^9.0.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -20802,6 +24653,12 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true + }, "stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -20819,6 +24676,12 @@ } } }, + "steno": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz", + "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", + "dev": true + }, "stickyfill": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stickyfill/-/stickyfill-1.1.1.tgz", @@ -20852,6 +24715,17 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -20894,6 +24768,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -20912,6 +24795,11 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, "styled-components": { "version": "6.0.7", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.0.7.tgz", @@ -20954,7 +24842,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -21079,6 +24966,12 @@ } } }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -21113,6 +25006,12 @@ "is-number": "^7.0.0" } }, + "totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true + }, "tough-cookie": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", @@ -21130,6 +25029,12 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true + }, "trough": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", @@ -21302,6 +25207,11 @@ "possible-typed-array-names": "^1.0.0" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "typescript": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", @@ -21326,6 +25236,11 @@ "which-boxed-primitive": "^1.0.2" } }, + "undici": { + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", + "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==" + }, "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -21447,7 +25362,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "requires": { "punycode": "^2.1.0" } @@ -21746,6 +25660,35 @@ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, + "winston": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz", + "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==", + "dev": true, + "requires": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + } + }, + "winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dev": true, + "requires": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + } + }, "word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -21767,6 +25710,17 @@ "strip-ansi": "^6.0.0" } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 32daa8b075..e4e8fe3154 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "unit:watch": "REDOCLY_TELEMETRY=off jest --watch", "coverage:cli": "npm run jest -- --roots packages/cli/src --coverage", "coverage:core": "npm run jest -- --roots packages/core/src --coverage", + "coverage:respect-core": "npm run jest -- --roots packages/respect-core/src --coverage", "typecheck": "tsc --noEmit --skipLibCheck", "e2e": "npm run webpack-bundle -- --mode=none && REDOCLY_TELEMETRY=off NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --roots=./__tests__/", "prettier": " npx prettier --write \"**/*.{ts,js,yaml,yml,json,md}\"", @@ -22,7 +23,7 @@ "eslint": "eslint packages/**", "clean": "rm -rf packages/**/lib packages/**/node_modules packages/**/*.tsbuildinfo package-lock.json node_modules dist && git checkout package-lock.json", "watch": "tsc -b tsconfig.build.json --watch ", - "compile": "tsc -b tsconfig.build.json", + "compile": "npm run respect:parser:generate && tsc -b tsconfig.build.json", "prepare": "npm run compile", "cli": "ts-node packages/cli/src/index.ts", "lint": "npm run cli lint resources/pets.yaml -- --format stylish", @@ -36,7 +37,9 @@ "release": "changeset publish", "upload": "node scripts/archive-and-upload-bundle.js", "deploy-local": "npm run webpack-bundle && npm run compile && ENV=local npm run upload", - "pack:prepare": "./scripts/local-pack.sh" + "pack:prepare": "./scripts/local-pack.sh", + "respect:parser:generate": "pegjs --format commonjs packages/respect-core/src/modules/runtime-expressions/abnf-parser.pegjs", + "json-server": "json-server --watch __tests__/respect/local-json-server/fake-db.json --port 3000 --host 0.0.0.0" }, "workspaces": [ "packages/*" @@ -72,8 +75,10 @@ "jest": "^29.0.0", "jest-environment-jsdom": "^29.7.0", "json-schema-to-ts": "^3.0.0", + "json-server": "1.0.0-beta.3", "null-loader": "^4.0.0", "outdent": "^0.7.1", + "pegjs": "0.10.0", "prettier": "^2.1.2", "slackify-markdown": "^4.3.1", "ts-jest": "^29.1.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 0954cfd732..41f39c62c1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -41,6 +41,7 @@ "chokidar": "^3.5.1", "colorette": "^1.2.0", "core-js": "^3.32.1", + "dotenv": "^16.4.7", "form-data": "^4.0.0", "get-port-please": "^3.0.1", "glob": "^7.1.6", diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 1e6d38dec2..0f4394a5ec 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,5 +1,6 @@ #!/usr/bin/env node - +import * as path from 'path'; +import * as dotenv from 'dotenv'; import './utils/assert-node-version'; import * as yargs from 'yargs'; import * as colors from 'colorette'; @@ -24,6 +25,7 @@ import { handleTranslations } from './commands/translations'; import { handleEject } from './commands/eject'; import { PRODUCT_PLANS } from './commands/preview-project/constants'; import { commonPushHandler } from './commands/push'; +import { handleRun } from '@redocly/respect-core'; import type { Arguments } from 'yargs'; import type { OutputFormat, RuleSeverity } from '@redocly/openapi-core'; @@ -32,6 +34,8 @@ import type { PushStatusOptions } from './reunite/commands/push-status'; import type { PushArguments } from './types'; import type { EjectOptions } from './commands/eject'; +dotenv.config({ path: path.resolve(__dirname, '../.env') }); + if (!('replaceAll' in String.prototype)) { require('core-js/actual/string/replace-all'); } @@ -863,6 +867,122 @@ yargs commandWrapper(handleEject)(argv as Arguments); } ) + .command( + 'respect [files..]', + 'Run workflow tests', + (yargs) => { + return yargs + .positional('files', { + describe: 'Test files or glob pattern', + type: 'string', + default: [], + }) + .env('REDOCLY_CLI_RESPECT') + .options({ + input: { + alias: 'i', + describe: 'Input parameters', + type: 'string', + }, + server: { + alias: 'S', + describe: 'Server parameters', + type: 'string', + }, + workflow: { + alias: 'w', + describe: 'Workflow name', + type: 'array', + greedy: false, + }, + skip: { + alias: 's', + describe: 'Workflow to skip', + type: 'array', + greedy: false, + }, + verbose: { + alias: 'v', + describe: 'Apply verbose mode', + type: 'boolean', + }, + 'har-output': { + describe: 'Har file output name', + type: 'string', + }, + 'json-output': { + describe: 'JSON file output name', + type: 'string', + }, + residency: { + describe: 'Residency of Reunite application. Defaults to US.', + type: 'string', + default: 'us', + }, + 'client-cert': { + describe: 'Mutual TLS client certificate', + type: 'string', + }, + 'client-key': { + describe: 'Mutual TLS client key', + type: 'string', + }, + 'ca-cert': { + describe: 'Mutual TLS CA certificate', + type: 'string', + }, + severity: { + describe: 'Severity of the check', + type: 'string', + }, + }); + }, + async (argv) => { + try { + await handleRun(argv); + } catch (err) { + // logger.error(red(`${err?.message}`)); + process.exit(1); + } + } + ) + .command( + 'generate-arazzo ', + 'Auto-generate test config file from description', + (yargs) => { + return yargs + .positional('descriptionPath', { + describe: 'Description file path', + type: 'string', + }) + .env('REDOCLY_SPOT') + .options({ + 'output-file': { + alias: 'o', + describe: 'Output File name', + type: 'string', + }, + extended: { + describe: + 'Generate config with populated values from description using success criteria', + type: 'boolean', + }, + residency: { + describe: 'Residency of Reunite application. Defaults to US.', + type: 'string', + default: 'us', + }, + }); + } + // async (argv) => { + // try { + // await handleGenerate(argv as GenerateConfigFileArgv); + // } catch { + // logger.error(`❌ Auto config generation failed.`); + // process.exit(1); + // } + // }, + ) .completion('completion', 'Generate autocomplete script for `redocly` command.') .demandCommand(1) .middleware([notifyUpdateCliVersion]) diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index e4c0bad886..e06ccf600f 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -4,6 +4,6 @@ "rootDir": "src", "outDir": "lib" }, - "references": [{ "path": "../core" }], + "references": [{ "path": "../core" }, { "path": "../respect-core" }], "exclude": ["lib"] } diff --git a/packages/core/src/logger.ts b/packages/core/src/logger.ts index 3d6232410e..f4ee51fa8a 100644 --- a/packages/core/src/logger.ts +++ b/packages/core/src/logger.ts @@ -2,7 +2,9 @@ import * as colorette from 'colorette'; import { isBrowser } from './env'; import { identity } from './utils'; -export { options as colorOptions } from 'colorette'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore this works but some types are not working +export const colorOptions = colorette.options; export const colorize = new Proxy(colorette, { get(target: typeof colorette, prop: string): typeof identity { diff --git a/packages/respect-core/.dockerignore b/packages/respect-core/.dockerignore new file mode 100644 index 0000000000..3c3629e647 --- /dev/null +++ b/packages/respect-core/.dockerignore @@ -0,0 +1 @@ +node_modules diff --git a/packages/respect-core/.env.example b/packages/respect-core/.env.example new file mode 100644 index 0000000000..91b1d8751e --- /dev/null +++ b/packages/respect-core/.env.example @@ -0,0 +1,3 @@ +SANDBOX_REBILLY_TOKEN= +SANDBOX_SLACK_TOKEN= +API_TEST_RUNNER_REDOCLY_AUTHORIZATION= \ No newline at end of file diff --git a/packages/respect-core/.gitignore b/packages/respect-core/.gitignore new file mode 100644 index 0000000000..fd54fa7960 --- /dev/null +++ b/packages/respect-core/.gitignore @@ -0,0 +1,7 @@ +node_modules +lib +lib-internal +build +.env +resources/fake-db.json +src/modules/runtime-expressions/abnf-parser.js diff --git a/packages/respect-core/.npmignore b/packages/respect-core/.npmignore new file mode 100644 index 0000000000..e0d99a6ed9 --- /dev/null +++ b/packages/respect-core/.npmignore @@ -0,0 +1,5 @@ +* +!lib/**/* +!bin/**/* +!README.md +!LICENSE diff --git a/packages/respect-core/CHANGELOG.md b/packages/respect-core/CHANGELOG.md new file mode 100644 index 0000000000..e0868e0f5e --- /dev/null +++ b/packages/respect-core/CHANGELOG.md @@ -0,0 +1,279 @@ +# @redocly/respect-core + +## 0.12.0 + +### Minor Changes + +- 47f2c39da7: Added support for the `QUERY` HTTP method, a new safe method with request body defined in [The HTTP QUERY Method specification](https://httpwg.org/http-extensions/draft-ietf-httpbis-safe-method-w-body.html). + +### Patch Changes + +- 16d4ccbe09: Updated version of the `@redocly/openapi-core` dependency to `1.28.3`. +- 40acc30481: Added support for floating-point numbers in Runtime Expressions. You can now use decimal numbers (like 3.14) in your expressions for validation and comparison operations. For example: `$response.body#/price == 3.14` or `$response.body#/value > 2.5`. + +## 0.11.2 + +### Patch Changes + +- 20aece6542: Updated version of the `@redocly/openapi-core` dependency to `1.28.2`. + +## 0.11.1 + +### Patch Changes + +- 0bce955e60: Improved error messages when source description files (OpenAPI or Arazzo) cannot be found at the specified paths. + +## 0.11.0 + +### Minor Changes + +- d403545209: Added configuration options to customize severity levels for checks. You can now set each check type to `error`, `warn`, or `off`, depending on your needs. + +## 0.10.3 + +### Patch Changes + +- 673dc80022: Enhanced Runtime Expression handler to support accessing response body data through the `outputs` syntax. You can now reference either specific parts of the response body or the entire body when working with output properties. + +## 0.10.2 + +### Patch Changes + +- ad4047544b: Removed support for `in: body` parameters due to Arazzo specification updates. + +## 0.10.1 + +### Patch Changes + +- ad2038e764: Removed deprecated `$message` variable from Runtime Expression context. + This variable was previously used for AsyncAPI support but is no longer needed in the current Arazzo specification. + +## 0.10.0 + +### Minor Changes + +- e8bcd3e0c5: - Added mTLS (Mutual TLS) support that enables certificate-based mutual authentication for secure API endpoint access in your workflows. + +## 0.9.1 + +### Patch Changes + +- 128d6e8fa4: - Improved HTTP request handling by migrating from `node-fetch` to `undici` package, providing better performance and native HTTP/2 support. + +## 0.9.0 + +### Minor Changes + +- e80a68de80: Added support for `in: body` parameters, enabling request body data to be set through individual parameters. This support provides a more granular way to define request body content alongside the existing `requestBody` field, and allows for better reusability of common body parameters across steps. + +## 0.8.0 + +### Minor Changes + +- 975c728885: Added support for `end` type actions. + When a workflow step has an `end` action, the workflow finishes and the context returns to the caller with applicable outputs. + +### Patch Changes + +- 698f536b5c: Updated version of the `@redocly/openapi-core` dependency to `1.26.1`. + +## 0.7.31 + +### Patch Changes + +- 7cd087a963: Added the `server` input Respect parameter so users can use it to override the server URL for each source description inside `sourceDescriptions`. + +## 0.7.30 + +### Patch Changes + +- f76b4a1ead: Made headers case-insensitive in runtime expressions. + +## 0.7.29 + +### Patch Changes + +- 266c7f2afc: Fixed a typo in the Ajv error message. + +## 0.7.28 + +### Patch Changes + +- eb6f81e9bb: Improved response validation by adding a default check to ensure the content type is defined in the description for the corresponding status code. +- 39baac3a33: Added a log entry of the duration of each workflow execution to the JSON output file. +- 2bdaa2b95f: Limited the context of `$steps` to have access only to individual step `outputs`. + +## 0.7.27 + +### Patch Changes + +- 1bc00e4fab: Changed JSON output results structure so that if it contains sensitive information, that information is masked. + +## 0.7.26 + +### Patch Changes + +- a24888e143: Removed the `x-expect` extension. + +## 0.7.25 + +### Patch Changes + +- 98365e2b57: Calculated response time of each workflow's step and displayed in output. + +## 0.7.24 + +### Patch Changes + +- ffef283ad6: Added a `--json-output` CLI option. + Using this option with a Respect `run` command saves the logs to a JSON file. + +## 0.7.23 + +### Patch Changes + +- e480913066: Changed logout command error handling. This prevents a potentially misleading error about a missing file from displaying. + +## 0.7.22 + +### Patch Changes + +- f58dcd53c5: Added validation of expressions at runtime according to the rules of Arazzo ABNF syntax. + +## 0.7.21 + +### Patch Changes + +- aeb04cf514: Renamed `headers` to `header` in the runtime expression context to align with the Arazzo specification. +- aeb04cf514: Made `successCriteria` source context to be aligned with the Arazzo runtime expressions. +- aeb04cf514: Added calculation of accept header based on OpenAPI description. + +## 0.7.20 + +### Patch Changes + +- 0ca5b3d742: Removed the `type:none` sourceDescriptions Arazzo extension. + +## 0.7.19 + +### Patch Changes + +- 9e9ddadacd: Removed the `x-inherit` extension. +- 5a24cae01d: Changed the `x-operation` extension in Arazzo, enabling users to make requests with this extension without an API description file. + +## 0.7.18 + +### Patch Changes + +- 3782979bb7: Removed the `x-parameters` extension. +- 7e5ff91872: Removed the `x-inputs` extension. + +## 0.7.17 + +### Patch Changes + +- bc731fc937: Removed `x-assert` extension. + +## 0.7.16 + +### Patch Changes + +- b85222cbea: Changed the schema check step to fail when `x-operation` doesn't exist in an OpenAPI description. + +## 0.7.15 + +### Patch Changes + +- 7bf2b08fb8: Improved error message format by adding clarifying details. + +## 0.7.14 + +### Patch Changes + +- e9dd91ffd7: Enabled the resolution of `serverUrl`s when a description file contains multiple `sourceDescriptions` and uses `operationPath` syntax. + +## 0.7.13 + +### Patch Changes + +- 7556afc3d7: Added masking of request/response secrets in verbose logs when the secrets are described in available OpenAPI description schemas. + +## 0.7.12 + +### Patch Changes + +- b91a1c1d11: Added reusable object support to allow referencing objects within the components section from inside a step or workflow. + +## 0.7.11 + +### Patch Changes + +- 29e51a927b: Fixed an issue where Respect didn’t resolve references inside the external OpenAPI description. + +## 0.7.10 + +### Patch Changes + +- 53836adf96: Updated the behavior of the `x-serverUrl` extension so that it overrides the server URL value for `operationPath` even when there is only one sourceDescription. +- 4f260a3780: Improved verbose logs to mask information about inputs with `password` format. + +## 0.7.9 + +### Patch Changes + +- f050e5a53e: Improved the residency login failure message to display the connection URL. + +## 0.7.8 + +### Patch Changes + +- 9db53ec9ec: Apply additional validation rules: 'no-criteria-xpath' and 'no-actions-type-end'. + +## 0.7.7 + +### Patch Changes + +- cdbed7a5bd: Removed CLI telemetry. + +## 0.7.6 + +### Patch Changes + +- d6153cc46b: Environment variables from your `.env` file are now accessible using the `$inputs.env` context, making it easier to use configuration values in your workflows. +- d6153cc46b: Added top-level `x-inputs` extention to apply inputs to each workflow in the spec. + +## 0.7.5 + +### Patch Changes + +- 5ac0191194: Fixed workflows run delay caused by sending telemetry. + +## 0.7.4 + +### Patch Changes + +- e97765475e: Fixed an issue where the auto-generated description path was invalid when Arazzo and OpenAPI files were located in different folders. + +## 0.7.3 + +### Patch Changes + +- d5992f9909: If a path to a description file in `SourceDescriptions` is invalid, the application returns an error message. + +## 0.7.2 + +### Patch Changes + +- 79c2db9b7d: Added error handling for the `residency` parameter and device login. + +## 0.7.1 + +### Patch Changes + +- 101f5ea4ad: Add support for `successActions` and `failureActions` for workflows. + +## 0.7.0 + +### Minor Changes + +- a752b85ebb: feat: Migrated Respect syntax to use Arazzo Specification. diff --git a/packages/respect-core/Dockerfile b/packages/respect-core/Dockerfile new file mode 100644 index 0000000000..740f84b3ba --- /dev/null +++ b/packages/respect-core/Dockerfile @@ -0,0 +1,16 @@ +# Run the fake db in an isolated environment +FROM node:22-alpine@sha256:f2dc6eea95f787e25f173ba9904c9d0647ab2506178c7b5b7c5a3d02bc4af145 + +# Set the working directory inside the container +WORKDIR /server + +# Copy the package.json file into the container +COPY package.json . + +# Install dependencies +RUN npm install --no-optional + +COPY . . + +# Run the Test Runner +CMD ["npm", "run", "json-server"] diff --git a/packages/respect-core/LICENSE b/packages/respect-core/LICENSE new file mode 100644 index 0000000000..5dea0b91ac --- /dev/null +++ b/packages/respect-core/LICENSE @@ -0,0 +1,18 @@ +(c) Copyright 2023 Redocly Inc. All rights reserved. + +Respect is subject to the following terms. + +Respect is a separate product and not included in any Redocly pricing plans. + +The following beta products terms apply in addition to the Redocly Subscription Agreement: + +FROM TIME TO TIME, CUSTOMER MAY HAVE THE OPTION TO PARTICIPATE IN A PROGRAM WITH REDOCLY +WHERE CUSTOMER GETS TO USE ALPHA OR BETA PRODUCTS, FEATURES OR DOCUMENTATION +(COLLECTIVELY, “BETA PRODUCTS”) OFFERED BY REDOCLY. THE BETA PRODUCTS ARE NOT GENERALLY +AVAILABLE AND ARE PROVIDED “AS IS”. NOTWITHSTANDING ANYTHING HEREIN, REDOCLY DOES NOT +PROVIDE ANY INDEMNITIES, SERVICE LEVEL, SUPPORT OR OTHER COMMITMENTS OR WARRANTIES, +EXPRESS OR IMPLIED, INCLUDING WARRANTIES OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT, AND +FITNESS FOR A PARTICULAR PURPOSE, IN RELATION THERETO. CUSTOMER OR REDOCLY MAY TERMINATE +CUSTOMER’S ACCESS TO THE BETA PRODUCTS AT ANY TIME. + +Redocly Subscription Agreement: https://redocly.com/subscription-agreement/ diff --git a/packages/respect-core/README.md b/packages/respect-core/README.md new file mode 100644 index 0000000000..88bd066a7e --- /dev/null +++ b/packages/respect-core/README.md @@ -0,0 +1,25 @@ +# Respect Your API. Monitor with Confidence + +## `@redocly/respect-core` + +Continuous API monitoring powered by OpenAPI Arazzo workflows. Get real-time insights, detect issues early, and receive automated alerts via email or Slack. + +The key benefits of `respect`: + +- Increases clarity and readability of tests. +- Autogenerates tests from OpenAPI to get started quickly. +- Maintain a positive reputation in your ecosystem (nobody likes when a 3rd party API change breaks their own software). +- Efficient because it links API requests to corresponding OpenAPI descriptions automatically. +- Shift left to catch problems fast before they are costly. Run on developer laptops and CICD. + +You **MUST** have a working API server running in order to run the tests because it sends real HTTP requests. + +## License + +See LICENSE included with package. + +For more details and beta access, contact team@redocly.com + +## Get started + +Contact team@redocly.com to request beta access. diff --git a/packages/respect-core/bin/e2e.sh b/packages/respect-core/bin/e2e.sh new file mode 100644 index 0000000000..0324500706 --- /dev/null +++ b/packages/respect-core/bin/e2e.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Start server +docker build -t jsonserver . +docker run -p 3000:3000 --name test-server -d jsonserver + +# Run API tests +pnpm run build:internal +pnpm run jest-e2e +CODE=$? + +# Stop server +docker stop test-server +docker rm test-server + +echo Exited with code $CODE. +exit $CODE diff --git a/packages/respect-core/bin/index.js b/packages/respect-core/bin/index.js new file mode 100755 index 0000000000..60c011af30 --- /dev/null +++ b/packages/respect-core/bin/index.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +require('../lib/cli.js'); diff --git a/packages/respect-core/esbuild.cjs b/packages/respect-core/esbuild.cjs new file mode 100644 index 0000000000..24f149eeb4 --- /dev/null +++ b/packages/respect-core/esbuild.cjs @@ -0,0 +1,23 @@ +const path = require('path'); + +// Build the project +require('esbuild').build({ + bundle: true, + outdir: path.resolve(__dirname, './lib'), + sourcemap: false, + minify: true, + entryPoints: { + index: path.resolve(__dirname, './src/index.ts'), + }, + platform: 'node', + format: 'cjs', + define: { 'import.meta.url': '_importMetaUrl' }, + banner: { + js: "const _importMetaUrl=require('url').pathToFileURL(__filename)", + }, + target: 'node18', + mainFields: ['main', 'module'], + resolveExtensions: ['.ts', '.js', '.json'], + outExtension: { '.js': '.js' }, + packages: 'external', +}); diff --git a/packages/respect-core/examples/museum-api/museum-api-test.yaml b/packages/respect-core/examples/museum-api/museum-api-test.yaml new file mode 100644 index 0000000000..ab2ce44844 --- /dev/null +++ b/packages/respect-core/examples/museum-api/museum-api-test.yaml @@ -0,0 +1,122 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API + description: >- + An imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body + - stepId: buy-ticket + description: >- + Buy a ticket for the museum by calling an external workflow from another Arazzo file. + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + outputs: + ticketId: $outputs.ticketId + - workflowId: events-crud + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: list-events + description: >- + Request the list of events. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + - stepId: create-event + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + requestBody: + payload: + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - $steps.list-events.outputs.events.0.dates.0 + - $steps.list-events.outputs.events.0.dates.1 + price: 0 + successCriteria: + - condition: $statusCode == 201 + - context: $response.body + condition: $.name == 'Mermaid Treasure Identification and Analysis' + type: jsonpath + outputs: + createdEventId: $response.body#/eventId + name: $response.body#/name + + - stepId: get-event-by-id + description: >- + Get the details of the event that was created in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/get' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + successCriteria: + - context: $statusCode + condition: '^200$' + type: regex + - stepId: update-event + description: >- + Update the created event with new details. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/patch' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + requestBody: + payload: + name: 'Orca Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 200 + - context: $response.body + condition: $.name == 'Orca Identification and Analysis' + type: + type: jsonpath + version: draft-goessner-dispatch-jsonpath-00 + outputs: + updatedEventId: $response.body#/eventId + - stepId: delete-event + description: >- + Delete the event that was updated in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/delete' + parameters: + - name: eventId + in: path + value: $steps.update-event.outputs.updatedEventId + successCriteria: + - condition: $statusCode == 204 diff --git a/packages/respect-core/examples/museum-api/museum-api-with-errors.yaml b/packages/respect-core/examples/museum-api/museum-api-with-errors.yaml new file mode 100644 index 0000000000..98fcaf666a --- /dev/null +++ b/packages/respect-core/examples/museum-api/museum-api-with-errors.yaml @@ -0,0 +1,755 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '201': + description: Success. + content: + html/text: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '205': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + sample: + type: string + example: 'sample' + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/packages/respect-core/examples/museum-api/museum-api.yaml b/packages/respect-core/examples/museum-api/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/packages/respect-core/examples/museum-api/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/packages/respect-core/examples/museum-api/museum-tickets.yaml b/packages/respect-core/examples/museum-api/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/packages/respect-core/examples/museum-api/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/packages/respect-core/jest.config.js b/packages/respect-core/jest.config.js new file mode 100644 index 0000000000..82b098bd56 --- /dev/null +++ b/packages/respect-core/jest.config.js @@ -0,0 +1,14 @@ +const defaultConfig = require('../../jest.config.js'); + +module.exports = { + ...defaultConfig, + transform: { + '^.+\\.[tj]sx?$': [ + 'ts-jest', + { + isolatedModules: true, + tsconfig: './tsconfig.json', + }, + ], + }, +}; diff --git a/packages/respect-core/jest.config.unit.js b/packages/respect-core/jest.config.unit.js new file mode 100644 index 0000000000..18527627f1 --- /dev/null +++ b/packages/respect-core/jest.config.unit.js @@ -0,0 +1,6 @@ +const defaultConfig = require('./jest.config.js'); + +module.exports = { + ...defaultConfig, + testPathIgnorePatterns: ['/node_modules/', '/e2e/__tests__/'], +}; diff --git a/packages/respect-core/package.json b/packages/respect-core/package.json new file mode 100644 index 0000000000..5d87276339 --- /dev/null +++ b/packages/respect-core/package.json @@ -0,0 +1,82 @@ +{ + "name": "@redocly/respect-core", + "version": "1.29.0", + "description": "API TEST FRAMEWORK", + "scripts": { + "json-server": "json-server --watch api-samples/fake-db.json --port 3000 --host 0.0.0.0" + }, + "main": "lib/index.js", + "types": "lib/index.d.ts", + "exports": { + ".": { + "require": "./lib/index.js", + "types": "./lib/index.d.ts" + } + }, + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" + }, + "engineStrict": true, + "license": "SEE LICENSE IN LICENSE", + "repository": { + "type": "git", + "url": "https://github.com/Redocly/redocly-cli.git" + }, + "homepage": "https://github.com/Redocly/redocly-cli", + "keywords": [ + "API Testing" + ], + "contributors": [ + "Roman Hotsiy (https://redocly.com/)" + ], + "devDependencies": { + "@types/concat-stream": "^2.0.3", + "@types/cookie": "0.6.0", + "@types/har-format": "^1.2.16", + "@types/jest": "^29.2.4", + "@types/js-yaml": "4.0.9", + "@types/json-pointer": "1.0.34", + "@types/node": "22.10.5", + "@types/set-cookie-parser": "2.4.10", + "@types/yargs": "17.0.32", + "esbuild": "0.17.15", + "jest": "^29.3.1", + "jest-snapshot": "^29.3.1", + "json-schema-to-ts": "^3.0.1", + "npm-dts": "1.3.12", + "rimraf": "5.0.7", + "ts-jest": "^29.0.3", + "typescript": "5.6.2" + }, + "dependencies": { + "@faker-js/faker": "^7.6.0", + "@jest/expect-utils": "^29.3.1", + "@redocly/ajv": "8.11.2", + "@redocly/openapi-core": "1.29.0", + "better-ajv-errors": "^1.2.0", + "colorette": "^2.0.20", + "concat-stream": "^2.0.0", + "cookie": "^0.7.2", + "dotenv": "16.4.5", + "form-data": "4.0.0", + "jest-diff": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "js-yaml": "4.1.0", + "json-pointer": "^0.6.2", + "jsonpath-plus": "^10.0.6", + "open": "^10.1.0", + "openapi-sampler": "^1.6.1", + "outdent": "^0.8.0", + "set-cookie-parser": "^2.3.5", + "undici": "^6.21.1", + "yargs": "^17.6.2" + }, + "files": [ + "lib" + ], + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + } +} diff --git a/packages/respect-core/src/arazzo-schema.ts b/packages/respect-core/src/arazzo-schema.ts new file mode 100644 index 0000000000..d0cfae01f1 --- /dev/null +++ b/packages/respect-core/src/arazzo-schema.ts @@ -0,0 +1,368 @@ +export const operationMethod = { + type: 'string', + enum: [ + 'get', + 'post', + 'put', + 'delete', + 'patch', + 'head', + 'options', + 'trace', + 'connect', + 'query', + 'GET', + 'POST', + 'PUT', + 'DELETE', + 'PATCH', + 'HEAD', + 'OPTIONS', + 'TRACE', + 'CONNECT', + 'QUERY', + ], +} as const; +const openAPISourceDescriptionSchema = { + type: 'object', + properties: { + name: { type: 'string' }, + type: { type: 'string', enum: ['openapi'] }, + url: { type: 'string' }, + 'x-serverUrl': { type: 'string' }, + }, + additionalProperties: false, + required: ['name', 'type', 'url'], +} as const; +const arazzoSourceDescriptionSchema = { + type: 'object', + properties: { + name: { type: 'string' }, + type: { type: 'string', enum: ['arazzo'] }, + url: { type: 'string' }, + }, + additionalProperties: false, + required: ['name', 'type', 'url'], +} as const; +export const sourceDescriptionSchema = { + type: 'object', + oneOf: [openAPISourceDescriptionSchema, arazzoSourceDescriptionSchema], +} as const; +const sourceDescriptionsSchema = { + type: 'array', + items: sourceDescriptionSchema, +} as const; +export const extendedOperation = { + type: 'object', + properties: { + url: { type: 'string' }, + method: operationMethod, + }, + additionalProperties: false, + required: ['url', 'method'], +} as const; +export const reusableObject = { + type: 'object', + properties: { + reference: { type: 'string' }, + value: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], + }, + }, + required: ['reference'], + additionalProperties: false, +} as const; +export const parameter = { + type: 'object', + oneOf: [ + { + type: 'object', + properties: { + in: { type: 'string', enum: ['header', 'query', 'path', 'cookie'] }, + name: { type: 'string' }, + value: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], + }, + }, + required: ['name', 'value'], + additionalProperties: false, + }, + reusableObject, + ], +} as const; +const parameters = { + type: 'array', + items: parameter, +} as const; +export const infoObject = { + type: 'object', + properties: { + title: { type: 'string' }, + description: { type: 'string' }, + summary: { type: 'string' }, + version: { type: 'string' }, + }, + additionalProperties: false, + required: ['title', 'version'], +} as const; +export const replacement = { + type: 'object', + properties: { + target: { type: 'string' }, + value: { + oneOf: [ + { type: 'string' }, + { type: 'object' }, + { type: 'array' }, + { type: 'number' }, + { type: 'boolean' }, + ], + }, + }, +} as const; +export const requestBody = { + type: 'object', + properties: { + contentType: { type: 'string' }, + payload: { + oneOf: [ + { type: 'string' }, + { type: 'object', additionalProperties: true }, + { type: 'array' }, + { type: 'number' }, + { type: 'boolean' }, + ], + }, + encoding: { type: 'string' }, + replacements: { + type: 'array', + items: replacement, + }, + }, + additionalProperties: false, + required: ['payload'], +} as const; +export const criteriaObject = { + type: 'object', + properties: { + condition: { type: 'string' }, + context: { type: 'string' }, + type: { + oneOf: [ + { type: 'string', enum: ['regex', 'jsonpath', 'simple', 'xpath'] }, + { + type: 'object', + properties: { + type: { type: 'string', enum: ['jsonpath'] }, + version: { type: 'string', enum: ['draft-goessner-dispatch-jsonpath-00'] }, + }, + }, + { + type: 'object', + properties: { + type: { type: 'string', enum: ['xpath'] }, + version: { type: 'string', enum: ['xpath-30', 'xpath-20', 'xpath-10'] }, + }, + }, + ], + }, + }, + required: ['condition'], + additionalProperties: false, +} as const; +const criteriaObjects = { + type: 'array', + items: criteriaObject, +} as const; +export const onSuccessObject = { + type: 'object', + properties: { + name: { type: 'string' }, + type: { type: 'string', enum: ['goto', 'end'] }, + stepId: { type: 'string' }, + workflowId: { type: 'string' }, + criteria: criteriaObjects, + }, + additionalProperties: false, + required: ['type', 'name'], +} as const; +const onSuccessList = { + type: 'array', + items: { + oneOf: [onSuccessObject, reusableObject], + }, +} as const; +export const onFailureObject = { + type: 'object', + properties: { + name: { type: 'string' }, + type: { type: 'string', enum: ['goto', 'retry', 'end'] }, + workflowId: { type: 'string' }, + stepId: { type: 'string' }, + retryAfter: { type: 'number' }, + retryLimit: { type: 'number' }, + criteria: criteriaObjects, + }, + additionalProperties: false, + required: ['type', 'name'], +} as const; +const onFailureList = { + type: 'array', + items: { + oneOf: [onFailureObject, reusableObject], + }, +} as const; +export const step = { + type: 'object', + properties: { + stepId: { type: 'string' }, + description: { type: 'string' }, + operationId: { type: 'string' }, + operationPath: { type: 'string' }, + workflowId: { type: 'string' }, + parameters: parameters, + successCriteria: criteriaObjects, + onSuccess: onSuccessList, + onFailure: onFailureList, + outputs: { + type: 'object', + additionalProperties: { + oneOf: [ + { + type: 'string', + }, + { + type: 'object', + }, + { + type: 'array', + }, + { + type: 'boolean', + }, + { + type: 'number', + }, + ], + }, + }, + 'x-operation': extendedOperation, + requestBody: requestBody, + }, + required: ['stepId'], + oneOf: [ + { required: ['x-operation'] }, + { required: ['operationId'] }, + { required: ['operationPath'] }, + { required: ['workflowId'] }, + ], +} as const; +const steps = { + type: 'array', + items: step, +} as const; +const JSONSchema = { + type: 'object', + properties: { + type: { + type: 'string', + enum: ['object', 'array', 'string', 'number', 'integer', 'boolean', 'null'], + }, + format: { + type: 'string', + }, + properties: { + type: 'object', + additionalProperties: true, + }, + required: { + type: 'array', + items: { type: 'string' }, + }, + items: { + type: 'object', + additionalProperties: true, + }, + }, + required: ['type'], + additionalProperties: true, +} as const; +export const workflow = { + type: 'object', + properties: { + workflowId: { type: 'string' }, + summary: { type: 'string' }, + description: { type: 'string' }, + parameters: parameters, + dependsOn: { type: 'array', items: { type: 'string' } }, + inputs: JSONSchema, + outputs: { + type: 'object', + additionalProperties: { + type: 'string', + }, + }, + steps: steps, + successActions: { + type: 'array', + items: { + oneOf: [onSuccessObject, reusableObject], + }, + }, + failureActions: { + type: 'array', + items: onFailureObject, + }, + }, + additionalProperties: false, + required: ['workflowId', 'steps'], +} as const; +const workflows = { + type: 'array', + items: workflow, +} as const; +export const arazzoSchema = { + type: 'object', + properties: { + arazzo: { type: 'string', enum: ['1.0.0'] }, + info: infoObject, + sourceDescriptions: sourceDescriptionsSchema, + workflows: workflows, + components: { + type: 'object', + properties: { + inputs: { + type: 'object', + additionalProperties: { + type: 'object', + additionalProperties: true, + properties: { + type: { + type: 'string', + }, + properties: { + type: 'object', + additionalProperties: true, + }, + }, + required: ['type'], + }, + }, + parameters: { + type: 'object', + additionalProperties: parameter, + }, + successActions: { + type: 'object', + additionalProperties: onSuccessObject, + }, + failureActions: { + type: 'object', + additionalProperties: onFailureObject, + }, + }, + }, + }, + additionalProperties: false, + required: ['arazzo', 'info', 'sourceDescriptions', 'workflows'], +} as const; diff --git a/packages/respect-core/src/handlers/generate.ts b/packages/respect-core/src/handlers/generate.ts new file mode 100644 index 0000000000..638a73313f --- /dev/null +++ b/packages/respect-core/src/handlers/generate.ts @@ -0,0 +1,21 @@ +import { blue, yellow, gray } from 'colorette'; +import { writeFileSync } from 'fs'; +import { stringifyYaml } from '../utils/yaml'; +import { generateTestConfig } from '../modules/test-config-generator'; +import { DefaultLogger } from '../utils/logger/logger'; + +import type { GenerateConfigFileArgv } from '../types'; + +const logger = DefaultLogger.getInstance(); + +export async function handleGenerate(argv: GenerateConfigFileArgv) { + logger.log(gray('\n Generating test configuration... \n')); + + const generatedConfig = await generateTestConfig(argv as GenerateConfigFileArgv); + const content = stringifyYaml(generatedConfig); + + const fileName = argv?.outputFile || 'auto-generated.yaml'; + writeFileSync(fileName, content); + + logger.log('\n' + blue(`Config ${yellow(fileName)} successfully generated.`) + '\n'); +} diff --git a/packages/respect-core/src/handlers/index.ts b/packages/respect-core/src/handlers/index.ts new file mode 100644 index 0000000000..13f71af5b6 --- /dev/null +++ b/packages/respect-core/src/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './run'; +export * from './generate'; diff --git a/packages/respect-core/src/handlers/run.ts b/packages/respect-core/src/handlers/run.ts new file mode 100644 index 0000000000..ed4e0bcbc8 --- /dev/null +++ b/packages/respect-core/src/handlers/run.ts @@ -0,0 +1,91 @@ +import { bgRed, red } from 'colorette'; +import { runTestFile } from '../modules/flow-runner'; +import { + displayErrors, + displaySummary, + displayFilesSummaryTable, + calculateTotals, +} from '../modules/cli-output'; +import { DefaultLogger } from '../utils/logger/logger'; + +import type { RunArgv } from '../types'; + +const logger = DefaultLogger.getInstance(); +export async function handleRun(argv: any) { + const harOutputFile = argv['har-output']; + if (harOutputFile && !harOutputFile.endsWith('.har')) { + exitWithErrorMsg('File for HAR logs should be in .har format', 1); + } + + const jsonOutputFile = argv['json-output']; + if (jsonOutputFile && !jsonOutputFile.endsWith('.json')) { + exitWithErrorMsg('File for JSON logs should be in .json format', 1); + } + + const { skip, workflow } = argv; + + if (skip && workflow) { + logger.printNewLine(); + logger.log(red(`Cannot use both --skip and --workflow flags at the same time.`)); + return; + } + + try { + const startedAt = performance.now(); + const testsRunProblemsStatus: boolean[] = []; + const { files } = argv; + const runAllFilesResult = []; + + if (files.length > 1 && (jsonOutputFile || harOutputFile)) { + // TODO: implement multiple run files logs output + exitWithErrorMsg( + 'Currently only a single file can be run with --har-output or --json-output. Please run a single file at a time.', + 1 + ); + } + + for (const path of files) { + const result = await runFile({ ...argv, file: path }, startedAt, { + harFile: harOutputFile, + jsonFile: jsonOutputFile, + }); + testsRunProblemsStatus.push(result.hasProblems); + runAllFilesResult.push(result); + } + + logger.printNewLine(); + displayFilesSummaryTable(runAllFilesResult); + logger.printNewLine(); + + if (testsRunProblemsStatus.some((problems) => problems)) { + exitWithErrorMsg(' Tests exited with error ', 1); + } + } catch (err) { + exitWithErrorMsg((err as Error)?.message ?? err, 1); + } +} + +async function runFile( + argv: RunArgv, + startedAt: number, + output: { harFile: string | undefined; jsonFile: string | undefined } +) { + const { workflows } = await runTestFile(argv as RunArgv, output); + + const totals = calculateTotals(workflows); + const hasProblems = totals.workflows.failed > 0; + + if (totals.steps.failed > 0 || totals.steps.warnings > 0 || totals.steps.skipped > 0) { + displayErrors(workflows); + } + + displaySummary(startedAt, workflows, argv); + + return { hasProblems, file: argv.file, workflows, argv }; +} + +const exitWithErrorMsg = (message: string, code: 0 | 1 = 1) => { + logger.error(bgRed(message)); + logger.printNewLine(); + process.exit(code); +}; diff --git a/packages/respect-core/src/index.ts b/packages/respect-core/src/index.ts new file mode 100644 index 0000000000..7f4179d3fb --- /dev/null +++ b/packages/respect-core/src/index.ts @@ -0,0 +1 @@ +export { handleGenerate, handleRun } from './handlers/index'; diff --git a/packages/respect-core/src/modules/__tests__/checks/severity.test.ts b/packages/respect-core/src/modules/__tests__/checks/severity.test.ts new file mode 100644 index 0000000000..5d1e49812d --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/checks/severity.test.ts @@ -0,0 +1,41 @@ +import { resolveSeverityConfiguration, DEFAULT_SEVERITY_CONFIGURATION } from '../../checks'; + +describe('resolveSeverityConfiguration', () => { + it('should return the default severity configuration if no severity argument is provided', () => { + const result = resolveSeverityConfiguration(undefined); + expect(result).toEqual(DEFAULT_SEVERITY_CONFIGURATION); + }); + + it('should return the severity configuration if a severity argument stringified object is provided', () => { + const result = resolveSeverityConfiguration('{"STATUS_CODE_CHECK":"off","SCHEMA_CHECK":"off"}'); + expect(result).toEqual({ + ...DEFAULT_SEVERITY_CONFIGURATION, + STATUS_CODE_CHECK: 'off', + SCHEMA_CHECK: 'off', + }); + }); + + it('should return the severity configuration if a severity argument list is provided', () => { + const result = resolveSeverityConfiguration(['STATUS_CODE_CHECK=off', 'SCHEMA_CHECK=off']); + expect(result).toEqual({ + ...DEFAULT_SEVERITY_CONFIGURATION, + STATUS_CODE_CHECK: 'off', + SCHEMA_CHECK: 'off', + }); + }); + + it('should return the severity configuration if a severity argument string is provided', () => { + const result = resolveSeverityConfiguration('STATUS_CODE_CHECK=off,SCHEMA_CHECK=off'); + expect(result).toEqual({ + ...DEFAULT_SEVERITY_CONFIGURATION, + STATUS_CODE_CHECK: 'off', + SCHEMA_CHECK: 'off', + }); + }); + + it('should throw an error if the severity configuration is not valid', () => { + expect(() => resolveSeverityConfiguration('invalid')).toThrow( + 'Failed to parse severity configuration' + ); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/auto-with-lint-errors.yaml b/packages/respect-core/src/modules/__tests__/config-parcer/auto-with-lint-errors.yaml new file mode 100644 index 0000000000..0d4e8aac1e --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/config-parcer/auto-with-lint-errors.yaml @@ -0,0 +1,34 @@ +workflowsSpecWRONG: 1.0.0 +info: + title: Cat Facts API + version: '1.0' +sourceDescriptions: + - name: testing_acme + type: openapi + url: ../../../../api-samples/testing-acme.yaml +workflows: + - workflowId: get-bird-workflow + parameters: + - in: header + name: IMF-KEY + value: test-key + steps: + - stepId: get-pets + parameters: + - name: status + in: query + value: 'available' + - $ref: '#/components/parameters/page' + x-operation: + url: http://localhost:3000/pets + method: get +components: + parameters: + page: + name: page + in: query + value: 32 + pageSize: + name: pageSize + in: query + value: 10 diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/auto.yaml b/packages/respect-core/src/modules/__tests__/config-parcer/auto.yaml new file mode 100644 index 0000000000..393017f842 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/config-parcer/auto.yaml @@ -0,0 +1,34 @@ +arazzo: 1.0.1 +info: + title: Cat Facts API + version: '1.0' +sourceDescriptions: + - name: testing_acme + type: openapi + url: ../../../../api-samples/testing-acme.yaml +workflows: + - workflowId: get-bird-workflow + parameters: + - in: header + name: IMF-KEY + value: test-key + steps: + - stepId: get-pets + parameters: + - name: status + in: query + value: 'available' + - $ref: '#/components/parameters/page' + x-operation: + url: http://localhost:3000/pets + method: get +components: + parameters: + page: + name: page + in: query + value: 32 + pageSize: + name: pageSize + in: query + value: 10 diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/get-value-from-context.test.ts b/packages/respect-core/src/modules/__tests__/config-parcer/get-value-from-context.test.ts new file mode 100644 index 0000000000..ea42de51af --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/config-parcer/get-value-from-context.test.ts @@ -0,0 +1,216 @@ +import type { TestContext } from '../../../types'; + +import { createFaker } from '../../faker'; +import { getValueFromContext, resolvePath } from '../../config-parser'; + +describe('getValueFromContext', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return value from context', () => { + const ctx = { + $some: 'test', + } as unknown as TestContext; + expect(getValueFromContext('$some', ctx)).toEqual('test'); + }); + + it('should return custom list of objects value', () => { + const ctx = { + $some: 'John Wick', + } as unknown as TestContext; + expect(getValueFromContext([{ name: '$some' }, { name: 'Jonny Mnemonic' }], ctx)).toEqual([ + { name: 'John Wick' }, + { name: 'Jonny Mnemonic' }, + ]); + }); + + it('should return custom list value', () => { + const ctx = { + $some: 'John Wick', + } as unknown as TestContext; + expect(getValueFromContext(['Jonny Mnemonic', 'John Wick'], ctx)).toEqual([ + 'Jonny Mnemonic', + 'John Wick', + ]); + }); + + it('should return custom object value', () => { + const ctx = { + $some: 'John', + } as unknown as TestContext; + expect(getValueFromContext({ name: '$some', lastname: 'Mnemonic' }, ctx)).toEqual({ + name: 'John', + lastname: 'Mnemonic', + }); + }); + + it('should return complex value from context', () => { + const ctx = { + $env: { + token: 'test', + }, + } as unknown as TestContext; + expect(getValueFromContext('bearer {$env.token}', ctx)).toEqual('bearer test'); + }); + + it('should return multiple complex value from context', () => { + const ctx = { + $env: { + token: 'test', + }, + $faker: createFaker(), + } as unknown as TestContext; + expect( + getValueFromContext('bearer {$env.token} {$faker.number.integer({min:5,max:5})}', ctx) + ).toEqual('bearer test 5'); + }); + + it('should return complex object string from context', () => { + const ctx = { + $env: { + token: 'test', + }, + $faker: createFaker(), + } as unknown as TestContext; + expect( + getValueFromContext( + ' "name": "respect-test-project-name-{$faker.number.integer({ min: 5, max: 5 })}::{$faker.number.integer({ min: 5, max: 5 })}",', + ctx + ) + ).toEqual(' "name": "respect-test-project-name-5::5",'); + }); + + it('should return original value if can not resolve from context', () => { + const ctx = { + secret: { + token: 'test', + }, + } as unknown as TestContext; + expect(getValueFromContext('custom.domain.com', ctx)).toEqual('custom.domain.com'); + }); + + it('should return faker generated data', () => { + const ctx = { + $faker: createFaker(), + } as unknown as TestContext; + expect(getValueFromContext('$faker.number.integer({ min: 5, max: 5 })', ctx)).toEqual(5); + }); + + it('should return faker generated data with additional text', () => { + const ctx = { + $faker: createFaker(), + } as unknown as TestContext; + expect(getValueFromContext('number is {$faker.number.integer({min:5,max:5})}', ctx)).toEqual( + 'number is 5' + ); + }); + + it('should return faker generated data', () => { + const ctx = { + $faker: createFaker(), + } as unknown as TestContext; + expect(getValueFromContext('city is {$faker.address.city}', ctx)).not.toMatch( + '{$faker.address.city}' + ); + }); + + it('should remove `$file` identifier from the value', () => { + const ctx = { + $faker: createFaker(), + } as unknown as TestContext; + expect(getValueFromContext("$file('./test.yaml')", ctx)).toEqual('./test.yaml'); + }); + + // $sourceDescriptions..workflows. + it('should return workflow from $sourceDescriptions', () => { + const ctx = { + $sourceDescriptions: { + test: { + workflows: [ + { + workflowId: 'workflowTestId', + steps: [], + }, + ], + }, + }, + } as unknown as TestContext; + expect(getValueFromContext('$sourceDescriptions.test.workflows.workflowTestId', ctx)).toEqual({ + workflowId: 'workflowTestId', + steps: [], + }); + }); + + it('should return undefined from $sourceDescriptions if there is no such sourceDescription', () => { + const ctx = { + $sourceDescriptions: { + test: { + workflows: [ + { + workflowId: 'workflowTestId', + steps: [], + }, + ], + }, + }, + } as unknown as TestContext; + expect( + getValueFromContext('$sourceDescriptions.notExistingName.workflows.workflowTestId', ctx) + ).toEqual(undefined); + }); + + it('should return undefined from $sourceDescriptions if there is no workflowId provided', () => { + const ctx = { + $sourceDescriptions: { + test: { + workflows: [ + { + workflowId: 'workflowTestId', + steps: [], + }, + ], + }, + }, + } as unknown as TestContext; + expect(getValueFromContext('$sourceDescriptions.notExistingName.workflows.', ctx)).toEqual( + undefined + ); + }); + + it('should return undefined if getFakeData had error resolving value', () => { + const ctx = { + $sourceDescriptions: { + test: { + workflows: [ + { + workflowId: 'workflowTestId', + steps: [], + }, + ], + }, + }, + } as unknown as TestContext; + expect(getValueFromContext('{$faker.city}', ctx)).toEqual('undefined'); + }); +}); + +describe('parsePath', () => { + it('should return path params', () => { + const path = 'https://api.com/user/{id}/profile'; + expect( + resolvePath(path, { + id: 'test', + }) + ).toEqual('https://api.com/user/test/profile'); + }); + + it('should return same string if no path params', () => { + const path = 'https://api.com/user/profile'; + expect(resolvePath(path, {})).toEqual('https://api.com/user/profile'); + }); + + it('should return undefined if no path provided', () => { + expect(resolvePath(undefined, {})).toBeUndefined(); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/handle-request-body-replacements.test.ts b/packages/respect-core/src/modules/__tests__/config-parcer/handle-request-body-replacements.test.ts new file mode 100644 index 0000000000..65c9f15871 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/config-parcer/handle-request-body-replacements.test.ts @@ -0,0 +1,49 @@ +import { handlePayloadReplacements } from '../../config-parser'; + +describe('handlePayloadReplacements', () => { + it('should replace the value using JSON Pointer', () => { + const payload = { + foo: 'bar', + }; + const replacements = [ + { + target: '/foo', + value: 'baz', + }, + ]; + + handlePayloadReplacements(payload, replacements); + + expect(payload).toEqual({ + foo: 'baz', + }); + }); + + it('should throw an error if the RequestBody value is not found at JSON Pointer', () => { + const payload = { + foo: 'bar', + }; + const replacements = [ + { + target: '/bar', + value: 'baz', + }, + ]; + + expect(() => handlePayloadReplacements(payload, replacements)).toThrowError( + 'Invalid JSON Pointer: /bar' + ); + }); + + it('should throw an error if the target is not a string', () => { + const payload = { + foo: 'bar', + }; + const replacements = [{ target: 1, value: 'baz' }]; + + // @ts-expect-error + expect(() => handlePayloadReplacements(payload, replacements)).toThrowError( + 'Invalid JSON Pointer: 1' + ); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/map-header-params-and-cookie-to-object.test.ts b/packages/respect-core/src/modules/__tests__/config-parcer/map-header-params-and-cookie-to-object.test.ts new file mode 100644 index 0000000000..c96e8e9c55 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/config-parcer/map-header-params-and-cookie-to-object.test.ts @@ -0,0 +1,71 @@ +import { mapHeaderParamsAndCookieToObject } from '../../config-parser/map-header-params-and-cookie-to-object'; + +describe('mapHeaderParamsAndCookieToObject', () => { + it('should map header parameters correctly', () => { + const headerParams = { + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + }; + const result = mapHeaderParamsAndCookieToObject(headerParams); + expect(result).toEqual({ + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + }); + }); + + it('should map cookies correctly', () => { + const headerParams = { + Cookie: 'sessionId=abc123; userId=xyz789', + }; + const result = mapHeaderParamsAndCookieToObject(headerParams); + expect(result).toEqual({ + sessionId: 'abc123', + userId: 'xyz789', + }); + }); + + it('should handle mixed headers and cookies', () => { + const headerParams = { + 'Content-Type': 'application/json', + Cookie: 'sessionId=abc123; userId=xyz789', + Authorization: 'Bearer token', + }; + const result = mapHeaderParamsAndCookieToObject(headerParams); + expect(result).toEqual({ + 'Content-Type': 'application/json', + sessionId: 'abc123', + userId: 'xyz789', + Authorization: 'Bearer token', + }); + }); + + it('should handle empty cookie header', () => { + const headerParams = { + Cookie: '', + }; + const result = mapHeaderParamsAndCookieToObject(headerParams); + expect(result).toEqual({}); + }); + + it('should handle malformed cookie header', () => { + const headerParams = { + Cookie: 'sessionId=abc123; malformedCookie', + }; + const result = mapHeaderParamsAndCookieToObject(headerParams); + expect(result).toEqual({ + sessionId: 'abc123', + }); + }); + + it('should handle multiple cookies with spaces', () => { + const headerParams = { + Cookie: 'sessionId=abc123; userId=xyz789; token=abc def', + }; + const result = mapHeaderParamsAndCookieToObject(headerParams); + expect(result).toEqual({ + sessionId: 'abc123', + userId: 'xyz789', + token: 'abc def', + }); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/parse-parameters.test.ts b/packages/respect-core/src/modules/__tests__/config-parcer/parse-parameters.test.ts new file mode 100644 index 0000000000..bc9f78aafa --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/config-parcer/parse-parameters.test.ts @@ -0,0 +1,30 @@ +import { isParameterWithoutIn, isParameterWithIn } from '../../config-parser'; + +describe('isParameterWithoutIn', () => { + it('should return true if the parameter is not a parameter with in', () => { + const parameter = { name: 'test', value: 'test' }; + expect(isParameterWithoutIn(parameter)).toBe(true); + }); + + it('should return false if the parameter is a parameter with in', () => { + const parameter = { name: 'test', value: 'test', in: 'header' }; + expect(isParameterWithoutIn(parameter)).toBe(false); + }); + + it('should return false if the parameter is not a parameter', () => { + const parameter = 'test'; + expect(isParameterWithoutIn(parameter)).toBe(false); + }); +}); + +describe('isParameterWithIn', () => { + it('should return true if the parameter is a parameter with in', () => { + const parameter = { name: 'test', value: 'test', in: 'header' }; + expect(isParameterWithIn(parameter)).toBe(true); + }); + + it('should return false if the parameter is not a parameter', () => { + const parameter = 'test'; + expect(isParameterWithIn(parameter)).toBe(false); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/parse-request-body.test.ts b/packages/respect-core/src/modules/__tests__/config-parcer/parse-request-body.test.ts new file mode 100644 index 0000000000..7f99ca61f1 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/config-parcer/parse-request-body.test.ts @@ -0,0 +1,254 @@ +import * as fs from 'node:fs'; +import { Buffer } from 'node:buffer'; + +import type { RequestBody } from '../../../types'; + +import { parseRequestBody, stripFileDecorator } from '../../config-parser'; + +jest.mock('node:fs'); + +describe('parseRequestBody', () => { + it('should return empty object if no body', async () => { + expect(await parseRequestBody(undefined)).toEqual({}); + }); + + it('should return body with no ctx provided', async () => { + expect( + await parseRequestBody({ + payload: { + test: 'test', + }, + }) + ).toEqual({ + payload: { test: 'test' }, + contentType: undefined, + encoding: undefined, + }); + }); + + it('should return body with ctx provided', async () => { + expect( + await parseRequestBody({ + payload: 'clientId={$input.clientID}&grant_type=12', + contentType: 'application/x-www-form-urlencoded', + replacements: [ + { + target: '/clientId', + value: '123', + }, + ], + }) + ).toEqual({ + payload: { + clientId: '123', + grant_type: '12', + }, + contentType: 'application/x-www-form-urlencoded', + encoding: undefined, + }); + }); + + it('should return body with string replacement applied', async () => { + expect( + await parseRequestBody({ + payload: { + test: 'test', + }, + contentType: 'application/json', + encoding: 'utf-8', + }) + ).toEqual({ + payload: { test: 'test' }, + contentType: 'application/json', + encoding: 'utf-8', + }); + }); + + it('should handle multipart/form-data', async () => { + expect( + await parseRequestBody({ + payload: { + test: 'test', + }, + contentType: 'multipart/form-data', + encoding: 'utf-8', + }) + ).toEqual({ + payload: expect.any(Object), + contentType: expect.stringMatching( + 'multipart/form-data; boundary=--------------------------' + ), + encoding: 'utf-8', + }); + }); + + it('should handle multipart/form-data with file', async () => { + (fs.createReadStream as jest.Mock).mockReturnValueOnce('readStream'); + // @ts-ignore + (fs.access as jest.Mock).mockImplementation((_filePath, _mode, callback) => { + callback(); + }); + expect( + await parseRequestBody({ + payload: { + test: 'test', + file: "$file('file1.txt')", + }, + contentType: 'multipart/form-data', + encoding: 'utf-8', + }) + ).toEqual({ + payload: expect.any(Object), + contentType: expect.stringMatching( + 'multipart/form-data; boundary=--------------------------' + ), + encoding: 'utf-8', + }); + + jest.resetAllMocks(); + }); + + it('should handle multipart/form-data with nested object', async () => { + const { payload, contentType, encoding } = await parseRequestBody({ + payload: { + commit: { + message: 'test', + author: 'John Doe', + }, + }, + contentType: 'multipart/form-data', + encoding: 'utf-8', + }); + expect(contentType).toMatch('multipart/form-data; boundary=--------------------------'); + expect(encoding).toBe('utf-8'); + expect(typeof payload).toBe('object'); + + const expectedProperties = ['commit[message]', 'commit[author]']; + expectedProperties.forEach((prop) => { + expect(JSON.stringify(payload)).toContain(prop); + }); + }); + + it('should handle multipart/form-data with array', async () => { + expect( + await parseRequestBody({ + payload: { + test: 'test', + array: ['test', 'test2'], + }, + contentType: 'multipart/form-data', + encoding: 'utf-8', + }) + ).toEqual({ + payload: expect.any(Object), + contentType: expect.stringMatching( + 'multipart/form-data; boundary=--------------------------' + ), + encoding: 'utf-8', + }); + }); + + it('should handle multipart/form-data with array with file', async () => { + (fs.createReadStream as jest.Mock).mockReturnValueOnce('readStream'); + // @ts-ignore + (fs.access as jest.Mock).mockImplementation((_filePath, _mode, callback) => { + callback(); + }); + expect( + await parseRequestBody({ + payload: { + test: 'test', + array: ['test', "$file('file2.txt')"], + }, + contentType: 'multipart/form-data', + encoding: 'utf-8', + }) + ).toEqual({ + payload: expect.any(Object), + contentType: expect.stringMatching( + 'multipart/form-data; boundary=--------------------------' + ), + encoding: 'utf-8', + }); + jest.resetAllMocks(); + }); + + it('should handle multipart/form-data and return error reading file', async () => { + (fs.createReadStream as jest.Mock).mockReturnValueOnce('readStream'); + // @ts-ignore + (fs.access as jest.Mock).mockImplementation((_filePath, _mode, callback) => { + callback(new Error('error')); + }); + try { + await parseRequestBody({ + payload: { + test: 'test', + array: ['test', "$file('file2.txt')"], + }, + contentType: 'multipart/form-data', + encoding: 'utf-8', + }); + } catch (error) { + expect(error.message).toContain("file2.txt doesn't exist or isn't readable."); + } + }); + + it('should handle application/octet-stream', async () => { + (fs.createReadStream as jest.Mock).mockReturnValueOnce('readStream'); + // @ts-ignore + (fs.access as jest.Mock).mockImplementation((_filePath, _mode, callback) => { + callback(); + }); + expect( + await parseRequestBody({ + payload: "$file('file3.txt')", + contentType: 'application/octet-stream', + encoding: 'utf-8', + }) + ).toEqual({ + payload: 'readStream', + contentType: 'application/octet-stream', + encoding: 'utf-8', + }); + jest.resetAllMocks(); + }); + + it('should handle application/octet-stream with string payload', async () => { + expect( + await parseRequestBody({ + payload: new Buffer('test') as unknown as RequestBody['payload'], + contentType: 'application/octet-stream', + encoding: 'utf-8', + }) + ).toEqual({ + payload: new Buffer('test'), + contentType: 'application/octet-stream', + encoding: 'utf-8', + }); + }); + + it('should handle application/octet-stream and return error reading file', async () => { + (fs.createReadStream as jest.Mock).mockReturnValueOnce('readStream'); + // @ts-ignore + (fs.access as jest.Mock).mockImplementation((_filePath, _mode, callback) => { + callback(new Error('error')); + }); + await expect( + parseRequestBody({ + payload: "$file('file3.txt')", + contentType: 'application/octet-stream', + encoding: 'utf-8', + }) + ).rejects.toThrow("file3.txt doesn't exist or isn't readable."); + }); +}); + +describe('stripFileDecorator', () => { + it('should return file name without $file decorator', () => { + expect(stripFileDecorator("$file('file1.txt')")).toEqual('file1.txt'); + }); + + it('should return file name', () => { + expect(stripFileDecorator('file1.txt')).toEqual('file1.txt'); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/resolve-reusable-component-item.test.ts b/packages/respect-core/src/modules/__tests__/config-parcer/resolve-reusable-component-item.test.ts new file mode 100644 index 0000000000..12308fb16a --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/config-parcer/resolve-reusable-component-item.test.ts @@ -0,0 +1,76 @@ +import type { TestContext } from '../../../types'; + +import { resolveReusableComponentItem } from '../../config-parser'; + +describe('resolveReusableComponentItem', () => { + it('should return parameter if not reference', () => { + expect( + resolveReusableComponentItem({ in: 'query', name: 'test', value: 'test' }, {} as any) + ).toEqual({ + in: 'query', + name: 'test', + value: 'test', + }); + }); + + it('should return success action if not reference', () => { + expect( + resolveReusableComponentItem( + { + name: 'SuccessActio', + type: 'goto', + workflowId: 'test', + criteria: [{ condition: '$statusCode == 200' }], + }, + {} as unknown as TestContext + ) + ).toEqual({ + name: 'SuccessActio', + type: 'goto', + workflowId: 'test', + criteria: [{ condition: '$statusCode == 200' }], + }); + }); + + it('should throw an error if reference is not found', () => { + expect(() => + resolveReusableComponentItem({ reference: '$components.some.page' }, {} as any) + ).toThrow( + 'Invalid reference: available components are $components.parameters, $components.failureActions, or $components.successActions' + ); + }); + + it('should return parameter if reference is found', () => { + expect( + resolveReusableComponentItem({ reference: '$components.parameters.page' }, { + $components: { parameters: { page: { value: 'test', in: 'query', name: 'page' } } }, + } as unknown as TestContext) + ).toEqual({ + value: 'test', + in: 'query', + name: 'page', + }); + }); + + it('should return success action if reference is found', () => { + expect( + resolveReusableComponentItem({ reference: '$components.successActions.SuccessAction' }, { + $components: { + successActions: { + SuccessAction: { + name: 'SuccessAction', + type: 'goto', + workflowId: 'test', + criteria: [{ condition: '$statusCode == 200' }], + }, + }, + }, + } as unknown as TestContext) + ).toEqual({ + name: 'SuccessAction', + type: 'goto', + workflowId: 'test', + criteria: [{ condition: '$statusCode == 200' }], + }); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/resolve-reusable-object-reference.test.ts b/packages/respect-core/src/modules/__tests__/config-parcer/resolve-reusable-object-reference.test.ts new file mode 100644 index 0000000000..49756a9a97 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/config-parcer/resolve-reusable-object-reference.test.ts @@ -0,0 +1,84 @@ +import type { TestContext } from '../../../types'; + +import { resolveReusableObjectReference } from '../../config-parser/resolve-reusable-object-reference'; + +describe('resolveReusableObjectReference', () => { + it('should throw an error if the reference is invalid', () => { + expect(() => + resolveReusableObjectReference({ reference: '$components.inputs.test' }, {} as TestContext) + ).toThrow( + 'Invalid reference: available components are $components.parameters, $components.failureActions, or $components.successActions' + ); + }); + + it('should return the parameter if the reference is valid', () => { + expect( + resolveReusableObjectReference({ reference: '$components.parameters.test' }, { + $components: { parameters: { test: { value: 'test', in: 'query', name: 'test' } } }, + } as unknown as TestContext) + ).toEqual({ + value: 'test', + in: 'query', + name: 'test', + }); + }); + + it('should return the failure action if the reference is valid', () => { + expect( + resolveReusableObjectReference({ reference: '$components.failureActions.retryAction' }, { + $components: { + failureActions: { + retryAction: { + name: 'retryAction', + type: 'retry', + workflowId: 'final-workflow', + criteria: [{ condition: '$statusCode == 200' }], + }, + }, + }, + } as unknown as TestContext) + ).toEqual({ + name: 'retryAction', + type: 'retry', + workflowId: 'final-workflow', + criteria: [{ condition: '$statusCode == 200' }], + }); + }); + + it('should return the success action if the reference is valid', () => { + expect( + resolveReusableObjectReference( + { reference: '$components.successActions.gotoSuccessAction' }, + { + $components: { + successActions: { + gotoSuccessAction: { + name: 'gotoSuccessAction', + type: 'goto', + workflowId: 'final-workflow', + criteria: [{ condition: '$statusCode == 200' }], + }, + }, + }, + } as unknown as TestContext + ) + ).toEqual({ + name: 'gotoSuccessAction', + type: 'goto', + workflowId: 'final-workflow', + criteria: [{ condition: '$statusCode == 200' }], + }); + }); + + it('should override the value if the value is provided', () => { + expect( + resolveReusableObjectReference({ reference: '$components.parameters.test', value: '12' }, { + $components: { parameters: { test: { value: 'test', in: 'query', name: 'test' } } }, + } as unknown as TestContext) + ).toEqual({ + value: '12', + in: 'query', + name: 'test', + }); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/description-parser/get-operation-from-description.test.ts b/packages/respect-core/src/modules/__tests__/description-parser/get-operation-from-description.test.ts new file mode 100644 index 0000000000..66fdc21531 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/description-parser/get-operation-from-description.test.ts @@ -0,0 +1,403 @@ +import type { TestContext } from '../../../types'; + +import { cleanColors } from '../../../utils/clean-colors'; +import { + getOperationFromDescriptionBySource, + getOperationFromDescription, + DescriptionSource, +} from '../../description-parser'; +import { ApiFetcher } from '../../../utils/api-fetcher'; + +describe('getOperationFromDescriptionBySource', () => { + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const context = { + apiClient, + $sourceDescriptions: { + cats: { + paths: { + '/pet': { + get: { + operationId: 'getPet', + }, + post: { + operationId: 'postPet', + }, + }, + '/pet/{petId}': { + get: { + operationId: 'getPetById', + }, + delete: { + operationId: 'deletePetById', + }, + }, + }, + }, + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml', + }, + ], + } as unknown as TestContext; + + describe('when operationPath is provided', () => { + it('should return the operation if it exists', () => { + const operation = getOperationFromDescriptionBySource( + { + operationPath: + 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml#/paths/~1pet/get', + }, + context + ); + + expect(operation).toEqual({ + operationId: 'getPet', + method: 'get', + path: '/pet', + servers: undefined, + descriptionName: 'cats', + pathParameters: [], + }); + }); + + it('should return the operation if it exists in sourceDescriptions', () => { + const operation = getOperationFromDescriptionBySource( + { + operationPath: '$sourceDescriptions.cats#/paths/~1pet/get', + }, + context + ); + + expect(operation).toEqual({ + operationId: 'getPet', + method: 'get', + path: '/pet', + servers: undefined, + descriptionName: 'cats', + pathParameters: [], + }); + }); + + it('should throw an error if description does not exist in sourceDescriptions', () => { + try { + getOperationFromDescriptionBySource( + { + operationPath: + 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/not-existing.yaml#/paths/~1pet/post', + }, + context + ); + } catch (e) { + expect(cleanColors(e.message)).toEqual( + "Unknown operationPath https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/not-existing.yaml#/paths/~1pet/post. API description https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/not-existing.yaml is not listed in 'sourceDescriptions' workflow section." + ); + } + }); + + it('should throw an error if operation does not exist', () => { + try { + getOperationFromDescriptionBySource( + { + operationPath: + 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml#/paths/~1not-existing/get', + }, + context + ); + } catch (e) { + expect(e.message).toEqual('Invalid reference token: /not-existing'); + } + }); + + it('should throw an error if described sourceDescriptions missing', () => { + const context = { + $sourceDescriptions: { + cats: { + paths: { + '/pet': { + get: { + operationId: 'getPet', + }, + post: { + operationId: 'postPet', + }, + }, + '/pet/{petId}': { + get: { + operationId: 'cats.getPetById}', + }, + delete: { + operationId: 'deletePetById', + }, + }, + }, + }, + }, + } as unknown as TestContext; + try { + getOperationFromDescriptionBySource( + { + operationPath: + 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml#/paths/~1not-existing/get', + }, + context + ); + } catch (e) { + expect(e.message).toEqual('Missing described sourceDescriptions'); + } + }); + + it('should throw an error if Invalid fragment identifier', () => { + try { + getOperationFromDescriptionBySource( + { + operationPath: + 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml#/not-existing', + }, + context + ); + } catch (e) { + expect(cleanColors(e.message)).toEqual( + 'Invalid fragment identifier: /not-existing at operationPath https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml#/not-existing.' + ); + } + }); + + it('should return the operation if it exists based on autogenerated operationId', () => { + const operation = getOperationFromDescriptionBySource( + { + operationPath: + 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml#/paths/~1pet~1{petId}/get', + }, + context + ); + + expect(operation).toEqual({ + operationId: 'getPetById', + method: 'get', + path: '/pet/{petId}', + servers: undefined, + descriptionName: 'cats', + pathParameters: [], + }); + }); + }); + + describe('when operationId is provided', () => { + it('should return the operation if it exists', () => { + const operation = getOperationFromDescriptionBySource( + { + operationId: 'cats.getPet', + }, + context + ); + + expect(operation).toEqual({ + operationId: 'getPet', + method: 'get', + path: '/pet', + descriptionName: 'cats', + pathParameters: [], + servers: undefined, + }); + }); + + it('should return the operation from first available description if operationId does not have dot notation', () => { + const operation = getOperationFromDescriptionBySource( + { + operationId: 'getPet', + }, + context + ); + + expect(operation).toEqual({ + operationId: 'getPet', + method: 'get', + path: '/pet', + descriptionName: 'cats', + pathParameters: [], + servers: undefined, + }); + }); + + it('should return the operation if it exists based on autogenerated operationId', () => { + const operation = getOperationFromDescriptionBySource( + { + operationId: 'cats.getPetById', + }, + context + ); + + expect(operation).toEqual({ + operationId: 'getPetById', + method: 'get', + path: '/pet/{petId}', + descriptionName: 'cats', + pathParameters: [], + servers: undefined, + }); + }); + + it('should throw an error if the operation does not exist', () => { + try { + getOperationFromDescriptionBySource( + { + operationId: 'cats.getPetByStatus', + }, + context + ); + } catch (e) { + expect(cleanColors(e.message)).toEqual( + 'Unknown operationId getPetByStatus at cats.getPetByStatus.' + ); + } + }); + + it('should throw an error if the description does not exist', () => { + try { + getOperationFromDescriptionBySource( + { + operationId: 'catss.getPetByStatus', + }, + context + ); + } catch (e) { + expect(cleanColors(e.message)).toEqual( + 'Unknown description name catss at catss.getPetByStatus. Available descriptions: cats.' + ); + } + }); + + it('should return undefined if no path details available', () => { + const context = { + $sourceDescriptions: { + cats: { + paths: { + '/create': undefined, + }, + }, + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml', + }, + ], + } as unknown as TestContext; + + const operation = getOperationFromDescriptionBySource( + { + operationId: 'cats.create', + }, + context + ); + + expect(operation).toEqual(undefined); + }); + + it('should return operation if it exists from single description when operationId is like $sourceDescriptions.DESCRIPTION_NAME.OPERATION_ID', () => { + const operation = getOperationFromDescriptionBySource( + { + operationId: '$sourceDescriptions.cats.getPet', + }, + context + ); + + expect(operation).toEqual({ + descriptionName: 'cats', + operationId: 'getPet', + method: 'get', + path: '/pet', + pathParameters: [], + servers: undefined, + }); + }); + }); + + describe('when exactly one sourceDescription is provided', () => { + it('should return undefined if it exists from single description', () => { + const context = { + $sourceDescriptions: { + cats: { + paths: { + '/pet': { + get: { + operationId: 'getPet', + }, + post: { + operationId: 'postPet', + }, + }, + '/pet/{petId}': { + get: { + operationId: 'cats.getPetById', + }, + delete: { + operationId: 'deletePetById', + }, + }, + }, + }, + }, + sourceDescriptions: [], + } as unknown as TestContext; + + const operation = getOperationFromDescriptionBySource( + { + path: '/pet', + method: 'get', + } as unknown as DescriptionSource, + context + ); + + expect(operation).toEqual(undefined); + }); + }); + + describe('when source is not provided', () => { + it('should return undefined', () => { + const operation = getOperationFromDescriptionBySource({}, context); + expect(operation).toEqual(undefined); + }); + }); +}); + +describe('getOperationFromDescription', () => { + const descriptionPaths = { + '/pet': { + get: { + operationId: 'getPet', + }, + post: { + operationId: 'postPet', + }, + }, + '/pet/{petId}': { + get: { + operationId: 'getPetById', + }, + delete: { + operationId: 'deletePetById', + }, + }, + }; + + it('should return the operation if it exists', () => { + const operation = getOperationFromDescription('/pet', 'get', descriptionPaths); + + expect(operation).toEqual({ + operationId: 'getPet', + }); + }); + + it('should return {} if the operation does not exist', () => { + const operation = getOperationFromDescription('/pet', 'delete', descriptionPaths); + + expect(operation).toEqual(undefined); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/description-parser/get-request-body-schema.test.ts b/packages/respect-core/src/modules/__tests__/description-parser/get-request-body-schema.test.ts new file mode 100644 index 0000000000..e5bfbb7a92 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/description-parser/get-request-body-schema.test.ts @@ -0,0 +1,57 @@ +import { getRequestBodySchema, type OperationDetails } from '../../description-parser'; + +describe('getRequestBodySchema', () => { + it('should return the correct schema for a given request body', () => { + const descriptionOperation = { + requestBody: { + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + name: { type: 'string' }, + age: { type: 'integer' }, + }, + required: ['name'], + }, + }, + }, + }, + } as unknown as OperationDetails & Record; + + const schema = getRequestBodySchema('application/json', descriptionOperation); + expect(schema).toEqual({ + type: 'object', + properties: { + name: { type: 'string' }, + age: { type: 'integer' }, + }, + required: ['name'], + }); + }); + + it('should return undefined if the content type is not found', () => { + const descriptionOperation = { + requestBody: { + content: { + 'application/xml': { + schema: { + type: 'object', + properties: { + name: { type: 'string' }, + }, + }, + }, + }, + }, + } as unknown as OperationDetails & Record; + + const schema = getRequestBodySchema('application/json', descriptionOperation); + expect(schema).toBeUndefined(); + }); + + it('should return undefined if descriptionOperation is undefined', () => { + const schema = getRequestBodySchema('application/json', undefined); + expect(schema).toBeUndefined(); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/description-parser/get-response-schema.test.ts b/packages/respect-core/src/modules/__tests__/description-parser/get-response-schema.test.ts new file mode 100644 index 0000000000..d9317bf7ca --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/description-parser/get-response-schema.test.ts @@ -0,0 +1,113 @@ +import { getResponseSchema } from '../../description-parser'; + +describe('getResponseSchema', () => { + it('should return undefined if descriptionResponses is not provided', () => { + const result = getResponseSchema({ + statusCode: 200, + contentType: 'application/json', + }); + expect(result).toBeUndefined(); + }); + + it('should return undefined if statusCode is not found and no default response is provided', () => { + const result = getResponseSchema({ + statusCode: 404, + contentType: 'application/json', + descriptionResponses: { + 200: { + content: { + 'application/json': { + schema: { type: 'object' }, + }, + }, + }, + }, + }); + expect(result).toBeUndefined(); + }); + + it('should return the schema for the given statusCode and contentType', () => { + const result = getResponseSchema({ + statusCode: 200, + contentType: 'application/json', + descriptionResponses: { + 200: { + content: { + 'application/json': { + schema: { type: 'object' }, + }, + }, + }, + }, + }); + expect(result).toEqual({ type: 'object' }); + }); + + it('should return the schema for the default response if statusCode is not found', () => { + const result = getResponseSchema({ + statusCode: 404, + contentType: 'application/json', + descriptionResponses: { + default: { + content: { + 'application/json': { + schema: { type: 'object' }, + }, + }, + }, + }, + }); + expect(result).toEqual({ type: 'object' }); + }); + + it('should return undefined if contentType is not found in the response content', () => { + const result = getResponseSchema({ + statusCode: 200, + contentType: 'application/xml', + descriptionResponses: { + 200: { + content: { + 'application/json': { + schema: { type: 'object' }, + }, + }, + }, + }, + }); + expect(result).toBeUndefined(); + }); + + it('should return the schema for the default response if statusCode is not found and contentType matches', () => { + const result = getResponseSchema({ + statusCode: 404, + contentType: 'application/json', + descriptionResponses: { + default: { + content: { + 'application/json': { + schema: { type: 'object' }, + }, + }, + }, + }, + }); + expect(result).toEqual({ type: 'object' }); + }); + + it('should return undefined if neither statusCode nor default response is found', () => { + const result = getResponseSchema({ + statusCode: 404, + contentType: 'application/json', + descriptionResponses: { + 200: { + content: { + 'application/json': { + schema: { type: 'object' }, + }, + }, + }, + }, + }); + expect(result).toBeUndefined(); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/description-parser/remove-write-only-properties.test.ts b/packages/respect-core/src/modules/__tests__/description-parser/remove-write-only-properties.test.ts new file mode 100644 index 0000000000..cfbe934949 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/description-parser/remove-write-only-properties.test.ts @@ -0,0 +1,349 @@ +import { removeWriteOnlyProperties } from '../../description-parser'; + +describe('removeWriteOnlyProperties', () => { + it('should remove writeOnly properties', () => { + const schema = { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + }, + required: ['id', 'name'], + } as any; + + const result = removeWriteOnlyProperties(schema); + + expect(result).toEqual({ + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], + }); + }); + + it('should remove writeOnly properties from nested objects', () => { + const schema = { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + nested: { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + }, + required: ['id', 'name'], + }, + }, + required: ['id', 'name'], + } as any; + + const result = removeWriteOnlyProperties(schema); + + expect(result).toEqual({ + type: 'object', + properties: { + name: { + type: 'string', + }, + nested: { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], + }, + }, + required: ['name'], + }); + }); + + it('should remove writeOnly properties from nested arrays', () => { + const schema = { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + nested: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + }, + required: ['id', 'name'], + }, + }, + }, + required: ['id', 'name'], + } as any; + + const result = removeWriteOnlyProperties(schema); + + expect(result).toEqual({ + type: 'object', + properties: { + name: { + type: 'string', + }, + nested: { + type: 'array', + items: { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], + }, + }, + }, + required: ['name'], + }); + }); + + it('should remove writeOnly properties from additionalProperties', () => { + const schema = { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + }, + additionalProperties: { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + }, + required: ['id', 'name'], + }, + required: ['id', 'name'], + } as any; + + const result = removeWriteOnlyProperties(schema); + + expect(result).toEqual({ + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + additionalProperties: { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], + }, + required: ['name'], + }); + }); + + it('should remove writeOnly properties from oneOf', () => { + const schema = { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + }, + oneOf: [ + { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + }, + required: ['id', 'name'], + }, + ], + required: ['id', 'name'], + } as any; + + const result = removeWriteOnlyProperties(schema); + + expect(result).toEqual({ + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + oneOf: [ + { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], + }, + ], + required: ['name'], + }); + }); + + it('should remove writeOnly properties from allOf', () => { + const schema = { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + }, + allOf: [ + { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + }, + required: ['id', 'name'], + }, + ], + required: ['id', 'name'], + } as any; + + const result = removeWriteOnlyProperties(schema); + + expect(result).toEqual({ + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + allOf: [ + { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], + }, + ], + required: ['name'], + }); + }); + + it('should remove writeOnly properties from anyOf', () => { + const schema = { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + }, + anyOf: [ + { + type: 'object', + properties: { + id: { + type: 'string', + writeOnly: true, + }, + name: { + type: 'string', + }, + }, + required: ['id', 'name'], + }, + ], + required: ['id', 'name'], + } as any; + + const result = removeWriteOnlyProperties(schema); + + expect(result).toEqual({ + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + anyOf: [ + { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], + }, + ], + required: ['name'], + }); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/faker.test.ts b/packages/respect-core/src/modules/__tests__/faker.test.ts new file mode 100644 index 0000000000..46445ee885 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/faker.test.ts @@ -0,0 +1,100 @@ +import { createFaker } from '../faker'; + +describe('faker', () => { + const faker = createFaker(); + + it('should return a faker object', () => { + expect(faker).toBeDefined(); + }); + + it('should return a faker object with address', () => { + expect(faker.address).toBeDefined(); + }); + + it('should return a faker object with date', () => { + expect(faker.date).toBeDefined(); + }); + + it('should return a faker object with number', () => { + expect(faker.number).toBeDefined(); + }); + + it('should return a faker object with string', () => { + expect(faker.string).toBeDefined(); + }); + + describe('address', () => { + it('should return a city', () => { + expect(faker.address.city()).toBeDefined(); + }); + + it('should return a country', () => { + expect(faker.address.country()).toBeDefined(); + }); + + it('should return a zipCode', () => { + expect(faker.address.zipCode()).toBeDefined(); + }); + + it('should return a street', () => { + expect(faker.address.street()).toBeDefined(); + }); + }); + + describe('date', () => { + it('should return a past date', () => { + expect(faker.date.past()).toBeDefined(); + }); + + it('should return a future date', () => { + expect(faker.date.future()).toBeDefined(); + }); + }); + + describe('number', () => { + it('should return an integer', () => { + expect(faker.number.integer()).toBeDefined(); + }); + + it('should return a float', () => { + // @ts-ignore + expect(faker.number.float()).toBeDefined(); + }); + }); + + describe('string', () => { + it('should return an email', () => { + const email = faker.string.email(); + expect(email).toContain('.com'); + }); + + it('should return an email with provider and domain', () => { + const email = faker.string.email({ provider: 'test', domain: 'ua' }); + expect(email).toContain('test.ua'); + }); + + it('should return a username', () => { + expect(faker.string.userName()).toBeDefined(); + }); + + it('should return a first name', () => { + expect(faker.string.firstName()).toBeDefined(); + }); + + it('should return a last name', () => { + expect(faker.string.lastName()).toBeDefined(); + }); + + it('should return a full name', () => { + expect(faker.string.fullName()).toBeDefined(); + }); + + it('should return a uuid', () => { + expect(faker.string.uuid()).toBeDefined(); + }); + + it('should return a string', () => { + expect(faker.string.string()).toBeDefined(); + }); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/__snapshots__/run-step.test.ts.snap b/packages/respect-core/src/modules/__tests__/flow-runner/__snapshots__/run-step.test.ts.snap new file mode 100644 index 0000000000..0b525fa62a --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/__snapshots__/run-step.test.ts.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`runStep should run step and display checks 1`] = ` +[MockFunction] { + "calls": [ + [ + "GET http://localhost:3000/bird - step get-bird", + [ + { + "message": "", + "name": "status code check", + "pass": true, + "severity": "error", + }, + { + "message": "", + "name": "content-type check", + "pass": true, + "severity": "error", + }, + ], + undefined, + undefined, + ], + ], + "results": [ + { + "type": "return", + "value": undefined, + }, + ], +} +`; diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/call-api-and-analyze-results.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/call-api-and-analyze-results.test.ts new file mode 100644 index 0000000000..67b75b0b98 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/call-api-and-analyze-results.test.ts @@ -0,0 +1,382 @@ +import { fetch } from 'undici'; + +import type { TestContext } from '../../../types'; + +import { callAPIAndAnalyzeResults, DEFAULT_SEVERITY_CONFIGURATION } from '../../flow-runner'; +import { ApiFetcher } from '../../../utils/api-fetcher'; + +// @ts-ignore +jest.mock('undici'); + +describe('callAPIAndAnalyzeResults', () => { + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const ctx = { + apiClient, + $env: { + REDOCLY_DOMAIN: 'redocly.com', + }, + $faker: { + address: {}, + date: {}, + number: {}, + string: {}, + }, + $descriptions: { + cats: { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/fact': { + get: { + tags: ['Facts'], + summary: 'Get Random Fact', + description: 'Returns a random fact', + operationId: 'getRandomFact', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + '404': { + description: 'Fact not found', + }, + }, + }, + }, + '/facts': { + get: { + tags: ['Facts'], + summary: 'Get a list of facts', + description: 'Returns a a list of facts', + operationId: 'getFacts', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [ + { + url: 'https://catfact.ninja/', + }, + ], + info: { + title: 'Cat Facts API', + version: '1.0', + }, + }, + }, + workflows: [ + { + workflowId: 'get-breeds-workflow', + steps: [ + { + stepId: 'get-breeds-step', + operationId: 'cats.getBreeds', + checks: [], + response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Aegean', + country: 'Greece', + origin: 'Natural/Standard', + coat: 'Semi-long', + pattern: 'Bi- or tri-colored', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + code: 200, + headers: {}, + contentType: 'application/json', + }, + verboseLog: {}, + outputs: { + 'created-item-id': '1', + }, + }, + ], + }, + ], + $workflows: { + 'get-breeds-workflow': { + steps: { + 'get-breeds-step': { + request: { + headers: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + }, + }, + }, + }, + $steps: { + 'get-breeds-step': { + request: { + headers: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + outputs: { + 'created-item-id': '1', + }, + }, + }, + harLogs: {}, + options: { + workflowPath: 'simple.yaml', + harLogsFile: 'har-output', + metadata: { + _: [], + files: ['simple.yaml'], + $0: 'respect', + file: 'simple.yaml', + }, + }, + 'x-serverUrl': 'https://catfact.ninja/', + info: { + title: 'Cat Facts API', + version: '1.0', + }, + arazzo: '1.0.1', + severity: DEFAULT_SEVERITY_CONFIGURATION, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + $outputs: {}, + } as unknown as TestContext; + + it('should call API and return checks result', async () => { + const serverUrl = 'https://catfact.ninja/'; + const workflowName = 'get-breeds-workflow'; + const step = ctx.workflows[0].steps[0]; + + const mockResponse = { + status: 200, + json: jest.fn().mockResolvedValue({ id: 1 }), + text: jest.fn().mockResolvedValue(JSON.stringify({ id: 1 })), + headers: new Headers(), + }; + + // @ts-ignore + (fetch as jest.Mock).mockResolvedValue(mockResponse); + + const result = await callAPIAndAnalyzeResults({ + ctx, + workflowName, + step, + requestData: { + serverUrl: { url: serverUrl }, + path: '/breeds', + method: 'get', + parameters: [], + requestBody: {}, + openapiOperation: undefined, + }, + }); + expect(result).toEqual({ + expectCheck: true, + networkCheck: true, + successCriteriaCheck: true, + }); + + // @ts-ignore + (fetch as jest.Mock).mockRestore(); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/context/__snapshots__/create-runtime-expression-ctx.test.ts.snap b/packages/respect-core/src/modules/__tests__/flow-runner/context/__snapshots__/create-runtime-expression-ctx.test.ts.snap new file mode 100644 index 0000000000..64fb99e520 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/context/__snapshots__/create-runtime-expression-ctx.test.ts.snap @@ -0,0 +1,1511 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`createRuntimeExpressionCtx should create limited runtime expression context when step is not provided 1`] = ` +{ + "$components": {}, + "$faker": { + "address": { + "city": [Function], + "country": [Function], + "street": [Function], + "zipCode": [Function], + }, + "date": { + "future": [Function], + "past": [Function], + }, + "number": { + "float": [Function], + "integer": [Function], + }, + "string": { + "email": [Function], + "firstName": [Function], + "fullName": [Function], + "lastName": [Function], + "string": [Function], + "userName": [Function], + "uuid": [Function], + }, + }, + "$inputs": { + "env": {}, + }, + "$method": "", + "$outputs": {}, + "$request": undefined, + "$response": undefined, + "$sourceDescriptions": { + "cats": { + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "paths": { + "/breeds": { + "get": { + "description": "Returns a a list of breeds", + "operationId": "getBreeds", + "parameters": [ + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "Breed", + "properties": { + "breed": { + "description": "Breed", + "format": "string", + "title": "Breed", + "type": "string", + }, + "coat": { + "description": "Coat", + "format": "string", + "title": "Coat", + "type": "string", + }, + "country": { + "description": "Country", + "format": "string", + "title": "Country", + "type": "string", + }, + "origin": { + "description": "Origin", + "format": "string", + "title": "Origin", + "type": "string", + }, + "pattern": { + "description": "Pattern", + "format": "string", + "title": "Pattern", + "type": "string", + }, + }, + "title": "Breed model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of breeds", + "tags": [ + "Breeds", + ], + }, + }, + "/fact": { + "get": { + "description": "Returns a random fact", + "operationId": "getRandomFact", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + }, + }, + "description": "successful operation", + }, + "404": { + "description": "Fact not found", + }, + }, + "summary": "Get Random Fact", + "tags": [ + "Facts", + ], + }, + }, + "/facts": { + "get": { + "description": "Returns a a list of facts", + "operationId": "getFacts", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of facts", + "tags": [ + "Facts", + ], + }, + }, + }, + "servers": [ + { + "url": "https://catfact.ninja/", + }, + ], + }, + "catsTwo": { + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "paths": { + "/breeds": { + "get": { + "description": "Returns a a list of breeds", + "operationId": "getBreeds", + "parameters": [ + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "Breed", + "properties": { + "breed": { + "description": "Breed", + "format": "string", + "title": "Breed", + "type": "string", + }, + "coat": { + "description": "Coat", + "format": "string", + "title": "Coat", + "type": "string", + }, + "country": { + "description": "Country", + "format": "string", + "title": "Country", + "type": "string", + }, + "origin": { + "description": "Origin", + "format": "string", + "title": "Origin", + "type": "string", + }, + "pattern": { + "description": "Pattern", + "format": "string", + "title": "Pattern", + "type": "string", + }, + }, + "title": "Breed model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of breeds", + "tags": [ + "Breeds", + ], + }, + }, + "/fact": { + "get": { + "description": "Returns a random fact", + "operationId": "getRandomFact", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + }, + }, + "description": "successful operation", + }, + "404": { + "description": "Fact not found", + }, + }, + "summary": "Get Random Fact", + "tags": [ + "Facts", + ], + }, + }, + "/facts": { + "get": { + "description": "Returns a a list of facts", + "operationId": "getFacts", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of facts", + "tags": [ + "Facts", + ], + }, + }, + }, + "servers": [ + { + "url": "https://catfact.ninja/", + }, + ], + }, + "externalWorkflow": { + "arazzo": "1.0.1", + "components": {}, + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "sourceDescriptions": [ + { + "name": "cats", + "type": "openapi", + "url": "cats.yaml", + }, + ], + "workflows": [ + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getBreeds", + "stepId": "get-breeds-step", + }, + ], + "workflowId": "get-breeds-workflow", + }, + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getRandomFact", + "stepId": "get-fact-step", + }, + ], + "workflowId": "get-fact-workflow", + }, + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getFacts", + "stepId": "get-facts-step", + }, + ], + "workflowId": "get-facts-workflow", + }, + ], + }, + }, + "$statusCode": undefined, + "$steps": {}, + "$url": "", + "$workflows": { + "test": { + "inputs": { + "env": { + "AUTH_TOKEN": "1234567890", + }, + }, + "outputs": undefined, + "steps": { + "test": {}, + }, + }, + }, +} +`; + +exports[`createRuntimeExpressionCtx should create limited runtime expression context when workflowId and step provided 1`] = ` +{ + "$components": {}, + "$faker": { + "address": { + "city": [Function], + "country": [Function], + "street": [Function], + "zipCode": [Function], + }, + "date": { + "future": [Function], + "past": [Function], + }, + "number": { + "float": [Function], + "integer": [Function], + }, + "string": { + "email": [Function], + "firstName": [Function], + "fullName": [Function], + "lastName": [Function], + "string": [Function], + "userName": [Function], + "uuid": [Function], + }, + }, + "$inputs": { + "env": {}, + }, + "$method": undefined, + "$outputs": {}, + "$request": { + "body": {}, + "header": undefined, + "path": undefined, + "query": undefined, + }, + "$response": { + "body": {}, + "header": {}, + "path": undefined, + "query": undefined, + }, + "$sourceDescriptions": { + "cats": { + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "paths": { + "/breeds": { + "get": { + "description": "Returns a a list of breeds", + "operationId": "getBreeds", + "parameters": [ + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "Breed", + "properties": { + "breed": { + "description": "Breed", + "format": "string", + "title": "Breed", + "type": "string", + }, + "coat": { + "description": "Coat", + "format": "string", + "title": "Coat", + "type": "string", + }, + "country": { + "description": "Country", + "format": "string", + "title": "Country", + "type": "string", + }, + "origin": { + "description": "Origin", + "format": "string", + "title": "Origin", + "type": "string", + }, + "pattern": { + "description": "Pattern", + "format": "string", + "title": "Pattern", + "type": "string", + }, + }, + "title": "Breed model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of breeds", + "tags": [ + "Breeds", + ], + }, + }, + "/fact": { + "get": { + "description": "Returns a random fact", + "operationId": "getRandomFact", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + }, + }, + "description": "successful operation", + }, + "404": { + "description": "Fact not found", + }, + }, + "summary": "Get Random Fact", + "tags": [ + "Facts", + ], + }, + }, + "/facts": { + "get": { + "description": "Returns a a list of facts", + "operationId": "getFacts", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of facts", + "tags": [ + "Facts", + ], + }, + }, + }, + "servers": [ + { + "url": "https://catfact.ninja/", + }, + ], + }, + "catsTwo": { + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "paths": { + "/breeds": { + "get": { + "description": "Returns a a list of breeds", + "operationId": "getBreeds", + "parameters": [ + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "Breed", + "properties": { + "breed": { + "description": "Breed", + "format": "string", + "title": "Breed", + "type": "string", + }, + "coat": { + "description": "Coat", + "format": "string", + "title": "Coat", + "type": "string", + }, + "country": { + "description": "Country", + "format": "string", + "title": "Country", + "type": "string", + }, + "origin": { + "description": "Origin", + "format": "string", + "title": "Origin", + "type": "string", + }, + "pattern": { + "description": "Pattern", + "format": "string", + "title": "Pattern", + "type": "string", + }, + }, + "title": "Breed model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of breeds", + "tags": [ + "Breeds", + ], + }, + }, + "/fact": { + "get": { + "description": "Returns a random fact", + "operationId": "getRandomFact", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + }, + }, + "description": "successful operation", + }, + "404": { + "description": "Fact not found", + }, + }, + "summary": "Get Random Fact", + "tags": [ + "Facts", + ], + }, + }, + "/facts": { + "get": { + "description": "Returns a a list of facts", + "operationId": "getFacts", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of facts", + "tags": [ + "Facts", + ], + }, + }, + }, + "servers": [ + { + "url": "https://catfact.ninja/", + }, + ], + }, + "externalWorkflow": { + "arazzo": "1.0.1", + "components": {}, + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "sourceDescriptions": [ + { + "name": "cats", + "type": "openapi", + "url": "cats.yaml", + }, + ], + "workflows": [ + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getBreeds", + "stepId": "get-breeds-step", + }, + ], + "workflowId": "get-breeds-workflow", + }, + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getRandomFact", + "stepId": "get-fact-step", + }, + ], + "workflowId": "get-fact-workflow", + }, + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getFacts", + "stepId": "get-facts-step", + }, + ], + "workflowId": "get-facts-workflow", + }, + ], + }, + }, + "$statusCode": undefined, + "$steps": {}, + "$url": undefined, + "$workflows": { + "test": { + "inputs": { + "env": { + "AUTH_TOKEN": "1234567890", + }, + }, + "outputs": undefined, + "steps": { + "test": {}, + }, + }, + }, +} +`; + +exports[`createRuntimeExpressionCtx should create limited runtime expression context when workflowId is not provided 1`] = ` +{ + "$components": {}, + "$faker": { + "address": { + "city": [Function], + "country": [Function], + "street": [Function], + "zipCode": [Function], + }, + "date": { + "future": [Function], + "past": [Function], + }, + "number": { + "float": [Function], + "integer": [Function], + }, + "string": { + "email": [Function], + "firstName": [Function], + "fullName": [Function], + "lastName": [Function], + "string": [Function], + "userName": [Function], + "uuid": [Function], + }, + }, + "$inputs": { + "env": {}, + }, + "$method": "", + "$outputs": {}, + "$request": undefined, + "$response": undefined, + "$sourceDescriptions": { + "cats": { + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "paths": { + "/breeds": { + "get": { + "description": "Returns a a list of breeds", + "operationId": "getBreeds", + "parameters": [ + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "Breed", + "properties": { + "breed": { + "description": "Breed", + "format": "string", + "title": "Breed", + "type": "string", + }, + "coat": { + "description": "Coat", + "format": "string", + "title": "Coat", + "type": "string", + }, + "country": { + "description": "Country", + "format": "string", + "title": "Country", + "type": "string", + }, + "origin": { + "description": "Origin", + "format": "string", + "title": "Origin", + "type": "string", + }, + "pattern": { + "description": "Pattern", + "format": "string", + "title": "Pattern", + "type": "string", + }, + }, + "title": "Breed model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of breeds", + "tags": [ + "Breeds", + ], + }, + }, + "/fact": { + "get": { + "description": "Returns a random fact", + "operationId": "getRandomFact", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + }, + }, + "description": "successful operation", + }, + "404": { + "description": "Fact not found", + }, + }, + "summary": "Get Random Fact", + "tags": [ + "Facts", + ], + }, + }, + "/facts": { + "get": { + "description": "Returns a a list of facts", + "operationId": "getFacts", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of facts", + "tags": [ + "Facts", + ], + }, + }, + }, + "servers": [ + { + "url": "https://catfact.ninja/", + }, + ], + }, + "catsTwo": { + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "paths": { + "/breeds": { + "get": { + "description": "Returns a a list of breeds", + "operationId": "getBreeds", + "parameters": [ + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "Breed", + "properties": { + "breed": { + "description": "Breed", + "format": "string", + "title": "Breed", + "type": "string", + }, + "coat": { + "description": "Coat", + "format": "string", + "title": "Coat", + "type": "string", + }, + "country": { + "description": "Country", + "format": "string", + "title": "Country", + "type": "string", + }, + "origin": { + "description": "Origin", + "format": "string", + "title": "Origin", + "type": "string", + }, + "pattern": { + "description": "Pattern", + "format": "string", + "title": "Pattern", + "type": "string", + }, + }, + "title": "Breed model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of breeds", + "tags": [ + "Breeds", + ], + }, + }, + "/fact": { + "get": { + "description": "Returns a random fact", + "operationId": "getRandomFact", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + }, + }, + "description": "successful operation", + }, + "404": { + "description": "Fact not found", + }, + }, + "summary": "Get Random Fact", + "tags": [ + "Facts", + ], + }, + }, + "/facts": { + "get": { + "description": "Returns a a list of facts", + "operationId": "getFacts", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of facts", + "tags": [ + "Facts", + ], + }, + }, + }, + "servers": [ + { + "url": "https://catfact.ninja/", + }, + ], + }, + "externalWorkflow": { + "arazzo": "1.0.1", + "components": {}, + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "sourceDescriptions": [ + { + "name": "cats", + "type": "openapi", + "url": "cats.yaml", + }, + ], + "workflows": [ + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getBreeds", + "stepId": "get-breeds-step", + }, + ], + "workflowId": "get-breeds-workflow", + }, + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getRandomFact", + "stepId": "get-fact-step", + }, + ], + "workflowId": "get-fact-workflow", + }, + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getFacts", + "stepId": "get-facts-step", + }, + ], + "workflowId": "get-facts-workflow", + }, + ], + }, + }, + "$statusCode": undefined, + "$steps": {}, + "$url": "", + "$workflows": { + "test": { + "inputs": { + "env": { + "AUTH_TOKEN": "1234567890", + }, + }, + "outputs": undefined, + "steps": { + "test": {}, + }, + }, + }, +} +`; diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/context/__snapshots__/create-test-context.test.ts.snap b/packages/respect-core/src/modules/__tests__/flow-runner/context/__snapshots__/create-test-context.test.ts.snap new file mode 100644 index 0000000000..36d15c8866 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/context/__snapshots__/create-test-context.test.ts.snap @@ -0,0 +1,599 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`createTestContext should create context 1`] = ` +{ + "$components": {}, + "$faker": Any, + "$inputs": { + "env": {}, + }, + "$outputs": {}, + "$request": undefined, + "$response": undefined, + "$sourceDescriptions": { + "cats": { + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "paths": { + "/breeds": { + "get": { + "description": "Returns a a list of breeds", + "operationId": "getBreeds", + "parameters": [ + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "Breed", + "properties": { + "breed": { + "description": "Breed", + "format": "string", + "title": "Breed", + "type": "string", + }, + "coat": { + "description": "Coat", + "format": "string", + "title": "Coat", + "type": "string", + }, + "country": { + "description": "Country", + "format": "string", + "title": "Country", + "type": "string", + }, + "origin": { + "description": "Origin", + "format": "string", + "title": "Origin", + "type": "string", + }, + "pattern": { + "description": "Pattern", + "format": "string", + "title": "Pattern", + "type": "string", + }, + }, + "title": "Breed model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of breeds", + "tags": [ + "Breeds", + ], + }, + }, + "/fact": { + "get": { + "description": "Returns a random fact", + "operationId": "getRandomFact", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + }, + }, + "description": "successful operation", + }, + "404": { + "description": "Fact not found", + }, + }, + "summary": "Get Random Fact", + "tags": [ + "Facts", + ], + }, + }, + "/facts": { + "get": { + "description": "Returns a a list of facts", + "operationId": "getFacts", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of facts", + "tags": [ + "Facts", + ], + }, + }, + }, + "servers": [ + { + "url": "https://catfact.ninja/", + }, + ], + }, + "catsTwo": { + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "paths": { + "/breeds": { + "get": { + "description": "Returns a a list of breeds", + "operationId": "getBreeds", + "parameters": [ + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "Breed", + "properties": { + "breed": { + "description": "Breed", + "format": "string", + "title": "Breed", + "type": "string", + }, + "coat": { + "description": "Coat", + "format": "string", + "title": "Coat", + "type": "string", + }, + "country": { + "description": "Country", + "format": "string", + "title": "Country", + "type": "string", + }, + "origin": { + "description": "Origin", + "format": "string", + "title": "Origin", + "type": "string", + }, + "pattern": { + "description": "Pattern", + "format": "string", + "title": "Pattern", + "type": "string", + }, + }, + "title": "Breed model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of breeds", + "tags": [ + "Breeds", + ], + }, + }, + "/fact": { + "get": { + "description": "Returns a random fact", + "operationId": "getRandomFact", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + }, + }, + "description": "successful operation", + }, + "404": { + "description": "Fact not found", + }, + }, + "summary": "Get Random Fact", + "tags": [ + "Facts", + ], + }, + }, + "/facts": { + "get": { + "description": "Returns a a list of facts", + "operationId": "getFacts", + "parameters": [ + { + "description": "maximum length of returned fact", + "in": "query", + "name": "max_length", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + { + "description": "limit the amount of results returned", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "description": "CatFact", + "properties": { + "fact": { + "description": "Fact", + "format": "string", + "title": "Fact", + "type": "string", + }, + "length": { + "description": "Length", + "format": "int32", + "title": "Length", + "type": "integer", + }, + }, + "title": "CatFact model", + "type": "object", + }, + "type": "array", + }, + }, + }, + "description": "successful operation", + }, + }, + "summary": "Get a list of facts", + "tags": [ + "Facts", + ], + }, + }, + }, + "servers": [ + { + "url": "https://catfact.ninja/", + }, + ], + }, + "externalWorkflow": { + "arazzo": "1.0.1", + "components": {}, + "info": { + "title": "Cat Facts API", + "version": "1.0", + }, + "sourceDescriptions": [ + { + "name": "cats", + "type": "openapi", + "url": "cats.yaml", + }, + ], + "workflows": [ + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getBreeds", + "stepId": "get-breeds-step", + }, + ], + "workflowId": "get-breeds-workflow", + }, + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getRandomFact", + "stepId": "get-fact-step", + }, + ], + "workflowId": "get-fact-workflow", + }, + { + "steps": [ + { + "operationId": "$sourceDescriptions.cats.getFacts", + "stepId": "get-facts-step", + }, + ], + "workflowId": "get-facts-workflow", + }, + ], + }, + }, + "$steps": {}, + "$workflows": { + "test": { + "inputs": { + "env": { + "AUTH_TOKEN": "1234567890", + }, + }, + "outputs": undefined, + "steps": { + "test": {}, + }, + }, + }, + "apiClient": Any, + "arazzo": "1.0.1", + "harLogs": Any, + "info": { + "title": "API", + "version": "1.0", + }, + "mtlsCerts": undefined, + "options": { + "harLogsFile": "har-output", + "metadata": {}, + "verbose": false, + "workflow": undefined, + "workflowPath": "test.test.yaml", + }, + "secretFields": {}, + "severity": { + "CONTENT_TYPE_CHECK": "error", + "NETWORK_ERROR": "error", + "SCHEMA_CHECK": "error", + "STATUS_CODE_CHECK": "error", + "SUCCESS_CRITERIA_CHECK": "error", + "UNEXPECTED_ERROR": "error", + }, + "sourceDescriptions": [ + { + "name": "cats", + "type": "openapi", + "url": "__tests__/respect/cat-fact-api/cats.yaml", + }, + { + "name": "catsTwo", + "type": "openapi", + "url": "__tests__/respect/cat-fact-api/cats.yaml", + }, + { + "name": "externalWorkflow", + "type": "arazzo", + "url": "__tests__/respect/cat-fact-api/auto-cat.yaml", + }, + ], + "testDescription": { + "arazzo": "1.0.1", + "info": { + "title": "API", + "version": "1.0", + }, + "sourceDescriptions": [ + { + "name": "cats", + "type": "openapi", + "url": "__tests__/respect/cat-fact-api/cats.yaml", + }, + { + "name": "catsTwo", + "type": "openapi", + "url": "__tests__/respect/cat-fact-api/cats.yaml", + }, + { + "name": "externalWorkflow", + "type": "arazzo", + "url": "__tests__/respect/cat-fact-api/auto-cat.yaml", + }, + ], + "workflows": [ + { + "inputs": { + "properties": { + "env": { + "properties": { + "AUTH_TOKEN": { + "type": "string", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, + "steps": [ + { + "checks": [], + "operationId": "getCat", + "stepId": "test", + "successCriteria": [ + { + "condition": "$statusCode == 200", + }, + ], + }, + ], + "workflowId": "test", + }, + ], + }, + "workflows": [ + { + "inputs": { + "properties": { + "env": { + "properties": { + "AUTH_TOKEN": { + "type": "string", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, + "steps": [ + { + "checks": [], + "operationId": "getCat", + "stepId": "test", + "successCriteria": [ + { + "condition": "$statusCode == 200", + }, + ], + }, + ], + "workflowId": "test", + }, + ], +} +`; diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/context/create-runtime-expression-ctx.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/context/create-runtime-expression-ctx.test.ts new file mode 100644 index 0000000000..90ebf80378 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/context/create-runtime-expression-ctx.test.ts @@ -0,0 +1,116 @@ +import { Step, type AppOptions, type TestDescription } from '../../../../types'; +import { createRuntimeExpressionCtx, createTestContext } from '../../../flow-runner/context'; +import { ApiFetcher } from '../../../../utils/api-fetcher'; + +const testDescription = { + arazzo: '1.0.1', + info: { title: 'API', version: '1.0' }, + sourceDescriptions: [ + { name: 'cats', type: 'openapi', url: '__tests__/respect/cat-fact-api/cats.yaml' }, + { name: 'catsTwo', type: 'openapi', url: '__tests__/respect/cat-fact-api/cats.yaml' }, + { + name: 'externalWorkflow', + type: 'arazzo', + url: '__tests__/respect/cat-fact-api/auto-cat.yaml', + }, + ], + workflows: [ + { + workflowId: 'test', + inputs: { + type: 'object', + properties: { + env: { + type: 'object', + properties: { + AUTH_TOKEN: { type: 'string' }, + }, + }, + }, + }, + steps: [ + { + stepId: 'test', + operationId: 'getCat', + successCriteria: [{ condition: '$statusCode == 200' }], + }, + ], + }, + ], +} as unknown as TestDescription; + +const options = { + workflowPath: 'test.test.yaml', + workflow: undefined, + harLogsFile: 'har-output', + metadata: {}, + verbose: false, +} as AppOptions; + +process.env.AUTH_TOKEN = '1234567890'; + +// Unmock @redocly/openapi-core +jest.unmock('@redocly/openapi-core'); + +describe('createRuntimeExpressionCtx', () => { + it('should create limited runtime expression context when workflowId and step provided', async () => { + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const context = await createTestContext(testDescription, options, apiClient); + const runtimeExpressionContext = createRuntimeExpressionCtx({ + ctx: context, + workflowId: 'test', + step: { + stepId: 'test', + 'x-operation': { + method: 'get', + url: 'https://api.thecatapi.com/v1/images/search', + }, + requestData: { + headers: { + accept: 'application/json', + }, + method: 'get', + url: 'https://api.thecatapi.com/v1/images/search', + }, + } as unknown as Step, + }); + + expect(runtimeExpressionContext).toMatchSnapshot(); + expect(context.workflows).toBeDefined(); + expect((runtimeExpressionContext as any).workflows).toBeUndefined(); + }); + + it('should create limited runtime expression context when workflowId is not provided', async () => { + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const context = await createTestContext(testDescription, options, apiClient); + const runtimeExpressionContext = createRuntimeExpressionCtx({ + ctx: context, + step: { + stepId: 'test', + workflowId: 'test', + } as unknown as Step, + }); + + expect(runtimeExpressionContext).toMatchSnapshot(); + expect(context.workflows).toBeDefined(); + expect((runtimeExpressionContext as any).workflows).toBeUndefined(); + }); + it('should create limited runtime expression context when step is not provided', async () => { + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const context = await createTestContext(testDescription, options, apiClient); + const runtimeExpressionContext = createRuntimeExpressionCtx({ + ctx: context, + workflowId: 'test', + }); + + expect(runtimeExpressionContext).toMatchSnapshot(); + expect(context.workflows).toBeDefined(); + expect((runtimeExpressionContext as any).workflows).toBeUndefined(); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/context/create-test-context.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/context/create-test-context.test.ts new file mode 100644 index 0000000000..3a772ed41e --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/context/create-test-context.test.ts @@ -0,0 +1,870 @@ +import type { TestDescription, AppOptions, TestContext, Step } from '../../../../types'; + +import { ApiFetcher } from '../../../../utils/api-fetcher'; +import { + createTestContext, + DEFAULT_SEVERITY_CONFIGURATION, + collectSecretFields, +} from '../../../../modules/flow-runner'; + +// Unmock @redocly/openapi-core +jest.unmock('@redocly/openapi-core'); + +describe('createTestContext', () => { + it('should create context', async () => { + const testDescription = { + arazzo: '1.0.1', + info: { title: 'API', version: '1.0' }, + sourceDescriptions: [ + { name: 'cats', type: 'openapi', url: '__tests__/respect/cat-fact-api/cats.yaml' }, + { name: 'catsTwo', type: 'openapi', url: '__tests__/respect/cat-fact-api/cats.yaml' }, + { + name: 'externalWorkflow', + type: 'arazzo', + url: '__tests__/respect/cat-fact-api/auto-cat.yaml', + }, + ], + workflows: [ + { + workflowId: 'test', + inputs: { + type: 'object', + properties: { + env: { + type: 'object', + properties: { + AUTH_TOKEN: { type: 'string' }, + }, + }, + }, + }, + steps: [ + { + stepId: 'test', + operationId: 'getCat', + successCriteria: [{ condition: '$statusCode == 200' }], + }, + ], + }, + ], + } as unknown as TestDescription; + + const options = { + workflowPath: 'test.test.yaml', + workflow: undefined, + harLogsFile: 'har-output', + metadata: {}, + verbose: false, + } as AppOptions; + + process.env.AUTH_TOKEN = '1234567890'; + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const context = await createTestContext(testDescription, options, apiClient); + + expect(context).toMatchSnapshot({ + $components: {}, + $faker: expect.any(Object), + $sourceDescriptions: { + cats: { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { type: 'integer', format: 'int64' }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/facts': { + get: { + tags: ['Facts'], + summary: 'Get a list of facts', + description: 'Returns a a list of facts', + operationId: 'getFacts', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { type: 'integer', format: 'int64' }, + }, + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { type: 'integer', format: 'int64' }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/fact': { + get: { + tags: ['Facts'], + summary: 'Get Random Fact', + description: 'Returns a random fact', + operationId: 'getRandomFact', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { type: 'integer', format: 'int64' }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + '404': { description: 'Fact not found' }, + }, + }, + }, + }, + servers: [ + { + url: 'https://catfact.ninja/', + }, + ], + info: { title: 'Cat Facts API', version: '1.0' }, + }, + catsTwo: { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { type: 'integer', format: 'int64' }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/facts': { + get: { + tags: ['Facts'], + summary: 'Get a list of facts', + description: 'Returns a a list of facts', + operationId: 'getFacts', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { type: 'integer', format: 'int64' }, + }, + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { type: 'integer', format: 'int64' }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/fact': { + get: { + tags: ['Facts'], + summary: 'Get Random Fact', + description: 'Returns a random fact', + operationId: 'getRandomFact', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { type: 'integer', format: 'int64' }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + '404': { description: 'Fact not found' }, + }, + }, + }, + }, + servers: [ + { + url: 'https://catfact.ninja/', + }, + ], + info: { title: 'Cat Facts API', version: '1.0' }, + }, + externalWorkflow: { + info: { + title: 'Cat Facts API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'cats.yaml', + }, + ], + workflows: [ + { + steps: [ + { + operationId: '$sourceDescriptions.cats.getBreeds', + stepId: 'get-breeds-step', + }, + ], + workflowId: 'get-breeds-workflow', + }, + { + steps: [ + { + operationId: '$sourceDescriptions.cats.getRandomFact', + stepId: 'get-fact-step', + }, + ], + workflowId: 'get-fact-workflow', + }, + { + steps: [ + { + operationId: '$sourceDescriptions.cats.getFacts', + stepId: 'get-facts-step', + }, + ], + workflowId: 'get-facts-workflow', + }, + ], + arazzo: '1.0.1', + }, + }, + workflows: [ + { + workflowId: 'test', + inputs: { + type: 'object', + properties: { + env: { + type: 'object', + properties: { + AUTH_TOKEN: { type: 'string' }, + }, + }, + }, + }, + steps: [ + { + stepId: 'test', + operationId: 'getCat', + successCriteria: [{ condition: '$statusCode == 200' }], + checks: [], + }, + ], + }, + ], + $steps: {}, + $outputs: {}, + $workflows: { + test: { + inputs: { + env: { + AUTH_TOKEN: '1234567890', + }, + }, + outputs: undefined, + steps: { + test: {}, + }, + }, + }, + $inputs: { + env: {}, + }, + harLogs: expect.any(Object), + mtlsCerts: undefined, + options: { + workflowPath: 'test.test.yaml', + workflow: undefined, + harLogsFile: 'har-output', + metadata: {}, + verbose: false, + }, + info: { title: 'API', version: '1.0' }, + arazzo: '1.0.1', + secretFields: {}, + severity: DEFAULT_SEVERITY_CONFIGURATION, + sourceDescriptions: [ + { name: 'cats', type: 'openapi', url: '__tests__/respect/cat-fact-api/cats.yaml' }, + { name: 'catsTwo', type: 'openapi', url: '__tests__/respect/cat-fact-api/cats.yaml' }, + { + name: 'externalWorkflow', + type: 'arazzo', + url: '__tests__/respect/cat-fact-api/auto-cat.yaml', + }, + ], + apiClient: expect.any(ApiFetcher), + }); + + delete process.env.AUTH_TOKEN; + }); + + it('should clean environment variables', async () => { + process.env.TEST_VAR = 'test value'; + process.env.ANOTHER_VAR = 'another value'; + + const testDescription: TestDescription = { + arazzo: '1.0.1', + info: { title: 'API', version: '1.0' }, + workflows: [ + { + workflowId: 'test', + steps: [{ stepId: 'test', operationId: 'getCat' } as unknown as Step], + }, + ], + }; + + const options: AppOptions = { + workflowPath: 'test.test.yaml', + workflow: undefined, + harLogsFile: 'har-output', + metadata: {}, + verbose: false, + }; + + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const context = await createTestContext(testDescription, options, apiClient); + + expect(context.$workflows.test.inputs).toEqual(undefined); + + delete process.env.TEST_VAR; + delete process.env.ANOTHER_VAR; + }, 8000); + + it('should handle workflows with inputs and env variables', async () => { + const testDescription: TestDescription = { + arazzo: '1.0.1', + info: { title: 'API', version: '1.0' }, + workflows: [ + { + workflowId: 'test', + inputs: { + type: 'object', + properties: { + testInput: { type: 'string' }, + }, + }, + steps: [ + { + stepId: 'test', + operationId: 'getCat', + } as unknown as Step, + ], + }, + ], + }; + + const options: AppOptions = { + workflowPath: 'test.test.yaml', + input: JSON.stringify({ testInput: 'testValue' }), + workflow: undefined, + skip: undefined, + harLogsFile: 'har-output', + metadata: {}, + verbose: false, + }; + + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const context = await createTestContext(testDescription, options, apiClient); + + expect(context.$workflows).toMatchObject({ + test: { + inputs: { + testInput: 'testValue', + }, + outputs: undefined, + steps: { + test: expect.any(Object), + }, + }, + }); + }); + + it('should handle multiple workflows and set public workflows correctly', async () => { + const testDescription = { + arazzo: '1.0.1', + info: { title: 'API', version: '1.0' }, + workflows: [ + { + workflowId: 'workflow1', + inputs: { + type: 'object', + properties: { + input1: { type: 'string' }, + }, + }, + outputs: { + output1: { type: 'string' }, + }, + steps: [ + { + stepId: 'step1', + operationId: 'operation1', + }, + ], + }, + { + workflowId: 'workflow2', + steps: [ + { + stepId: 'step2', + operationId: 'operation2', + }, + ], + }, + ], + } as unknown as TestDescription; + + const options = { + workflowPath: 'test.test.yaml', + input: JSON.stringify({ input1: 'value1' }), + } as unknown as AppOptions; + + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const context = await createTestContext(testDescription, options, apiClient); + + expect(context.$workflows).toEqual({ + workflow1: { + inputs: { + input1: 'value1', + }, + outputs: { + output1: { type: 'string' }, + }, + steps: { + step1: {}, + }, + }, + workflow2: { + inputs: undefined, + outputs: undefined, + steps: { + step2: {}, + }, + }, + }); + }); + + it('should handle workflows without inputs', async () => { + const testDescription = { + arazzo: '1.0.1', + info: { title: 'API', version: '1.0' }, + workflows: [ + { + workflowId: 'workflow1', + steps: [{ stepId: 'step1', operationId: 'operation1' }], + }, + ], + } as unknown as TestDescription; + + const options = { + workflowPath: 'test.test.yaml', + } as unknown as AppOptions; + + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const context = await createTestContext(testDescription, options, apiClient); + + expect(context.$workflows.workflow1.inputs).toEqual(undefined); + }); + + it('should handle env variables', async () => { + const testDescription = { + arazzo: '1.0.1', + info: { title: 'API', version: '1.0' }, + workflows: [ + { + workflowId: 'workflow1', + inputs: { + type: 'object', + properties: { + env: { + type: 'object', + properties: { + ENV_VAR: { type: 'string' }, + }, + }, + }, + }, + steps: [{ stepId: 'step1', operationId: 'operation1' }], + }, + ], + } as unknown as TestDescription; + + const options = { + workflowPath: 'test.test.yaml', + } as unknown as AppOptions; + + process.env = { ENV_VAR: 'value' }; + + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const context = await createTestContext(testDescription, options, apiClient); + + expect(context.$workflows.workflow1?.inputs?.env?.ENV_VAR).toBe('value'); + + delete process.env.ENV_VAR; + }); +}); + +describe('storeSecretFields', () => { + it('should store secret fields with inputs', () => { + const ctx = { + secretFields: new Set(), + } as unknown as TestContext; + + const schema = { + type: 'object', + properties: { + password: { type: 'string', format: 'password' }, + nested: { + type: 'object', + properties: { + password: { type: 'string', format: 'password' }, + }, + }, + }, + }; + + const inputs = { + password: 'password', + nested: { + password: 'nested-password', + }, + }; + + collectSecretFields(ctx, schema, inputs); + + expect(ctx.secretFields).toEqual(new Set(['password', 'nested-password'])); + }); + + it('should store secret env fields', () => { + const ctx = { + secretFields: new Set(), + } as unknown as TestContext; + + const schema = { + type: 'object', + properties: { + env: { + type: 'object', + properties: { + password: { type: 'string', format: 'password' }, + nested: { + type: 'object', + properties: { + password: { type: 'string', format: 'password' }, + }, + }, + }, + }, + }, + }; + + const inputs = { + env: { + password: 'password', + nested: { + password: 'nested-password', + }, + }, + }; + + collectSecretFields(ctx, schema, inputs); + + expect(ctx.secretFields).toEqual(new Set(['password', 'nested-password'])); + }); + + it('should not store secret fields if not password format', () => { + const ctx = { + secretFields: new Set(), + } as unknown as TestContext; + + const schema = { + type: 'object', + properties: { + password: { type: 'string' }, + nested: { + type: 'object', + properties: { + password: { type: 'string' }, + }, + }, + }, + }; + + const inputs = { + password: 'password', + nested: { + password: 'nested-password', + }, + }; + + collectSecretFields(ctx, schema, inputs); + + expect(ctx.secretFields).toEqual(new Set()); + }); + + it('should store secret field with format password in object', () => { + const ctx = { + secretFields: new Set(), + } as unknown as TestContext; + + const schema = { + type: 'string', + format: 'password', + }; + + const inputs = { + tocken: 'secret-token', + }; + + collectSecretFields(ctx, schema, inputs, ['tocken']); + + expect(ctx.secretFields).toEqual(new Set(['secret-token'])); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/error-message-matcher.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/error-message-matcher.test.ts new file mode 100644 index 0000000000..5a0b253477 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/error-message-matcher.test.ts @@ -0,0 +1,20 @@ +import { bold } from 'colorette'; + +import { errorMessageMatcher } from '../../../modules/flow-runner'; + +describe('errorMessageMatcher', () => { + it('should return correct error message', () => { + const hint = 'hint'; + const generic = 'generic'; + const specific = 'specific'; + const result = errorMessageMatcher(hint, generic, specific); + expect(result).toEqual(`${hint}\n\n${bold('Matcher error')}: ${generic}\n\n${specific}`); + }); + + it('should return correct error message without specific', () => { + const hint = 'hint'; + const generic = 'generic'; + const result = errorMessageMatcher(hint, generic); + expect(result).toEqual(`${hint}\n\n${bold('Matcher error')}: ${generic}`); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/get-server-url.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/get-server-url.test.ts new file mode 100644 index 0000000000..de59d73740 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/get-server-url.test.ts @@ -0,0 +1,288 @@ +import type { ExtendedOperation, TestContext } from '../../../types'; +import type { OperationDetails } from '../../description-parser'; + +import { getServerUrl, GetServerUrlInput } from '../../flow-runner'; + +describe('getServerUrl', () => { + it('should return first server url from servers', () => { + const ctx = { + $sourceDescriptions: { + test: { servers: [{ url: 'https://example.com' }, { url: 'https://example2.com' }] }, + }, + } as unknown as TestContext; + const descriptionName = 'test'; + const result = getServerUrl({ ctx, descriptionName }); + expect(result).toEqual({ url: 'https://example.com' }); + }); + + it('should return undefined when path does not include url and no servers provided', () => { + const ctx = { + $sourceDescriptions: {}, + } as unknown as TestContext; + const descriptionName = 'test'; + const result = getServerUrl({ ctx, descriptionName }); + expect(result).toEqual(undefined); + }); + + it('should return server url from sourceDescription x-serverUrl', () => { + const ctx = { + sourceDescriptions: [ + { + name: 'test', + 'x-serverUrl': 'https://example.com', + }, + ], + $sourceDescriptions: { + test: { servers: [{ url: 'https://example.com' }] }, + }, + } as unknown as TestContext; + const descriptionName = 'test'; + const result = getServerUrl({ ctx, descriptionName }); + expect(result).toEqual({ url: 'https://example.com' }); + }); + + it('should return server url from sourceDescription x-serverUrl resolved from context', () => { + const ctx = { + sourceDescriptions: [ + { + name: 'test', + 'x-serverUrl': '$servers.0.url', + type: 'openapi', + }, + ], + $sourceDescriptions: { + test: { servers: [{ url: 'https://example.com' }] }, + }, + } as unknown as TestContext; + const descriptionName = 'test'; + const result = getServerUrl({ ctx, descriptionName }); + expect(result).toEqual({ url: 'https://example.com' }); + }); + + it('should return overwritten server url from sourceDescription x-serverUrl resolved from context', () => { + const ctx = { + sourceDescriptions: [ + { + name: 'test', + 'x-serverUrl': 'https://override.com', + type: 'openapi', + url: './openapi.yaml', + }, + ], + $sourceDescriptions: { + test: { servers: [{ url: 'https://example.com' }] }, + }, + } as unknown as TestContext; + const descriptionName = 'test'; + const result = getServerUrl({ ctx, descriptionName }); + expect(result).toEqual({ url: 'https://override.com' }); + }); + + it('should return server url from descriptionOperation', () => { + const ctx = { + $sourceDescriptions: { + test: { + paths: { + '/test': { + get: { + servers: [{ url: 'https://example1.com' }], + }, + }, + }, + }, + }, + } as unknown as TestContext; + const openapiOperation = { + servers: [{ url: 'https://example1.com' }], + } as unknown as OperationDetails & { servers: { url: string }[] }; + const descriptionName = 'test'; + const result = getServerUrl({ ctx, descriptionName, openapiOperation }); + expect(result).toEqual({ url: 'https://example1.com' }); + }); + + it('should return "x-operation" url as server url when descriptionName is not provided', () => { + const ctx = { + sourceDescriptions: [ + { + name: 'test2', + 'x-serverUrl': 'https://example2.com', + type: 'openapi', + }, + ], + $sourceDescriptions: { + test: { servers: [{ url: 'https://example.com' }] }, + }, + } as unknown as TestContext; + const descriptionName = ''; + const xOperation = { + url: 'http:/localhost:3000/test', + method: 'get', + } as unknown as ExtendedOperation; + const result = getServerUrl({ ctx, descriptionName, xOperation }); + expect(result).toEqual({ url: 'http:/localhost:3000/test' }); + }); + + it('should return x-serverUrl when available when descriptionName is not provided and there is only one sourceDescription', () => { + const ctx: TestContext = { + sourceDescriptions: [ + { + name: 'testApi', + type: 'openapi', + url: './openapi.yaml', + 'x-serverUrl': 'https://api.example.com', + }, + ], + $sourceDescriptions: {}, + } as unknown as TestContext; + + const result = getServerUrl({ ctx, descriptionName: '' }); + expect(result).toEqual({ url: 'https://api.example.com' }); + }); + + it('should return server URL from $sourceDescriptions when x-serverUrl is not available when descriptionName is not provided and there is only one sourceDescription', () => { + const ctx: TestContext = { + sourceDescriptions: [ + { + name: 'testApi', + type: 'openapi', + url: './openapi.yaml', + }, + ], + $sourceDescriptions: { + testApi: { + servers: [{ url: 'https://api.example.com' }], + }, + }, + } as unknown as TestContext; + + const result = getServerUrl({ ctx, descriptionName: '' }); + expect(result).toEqual({ url: 'https://api.example.com' }); + }); + + it('should return undefined when neither x-serverUrl nor $sourceDescriptions server is available when descriptionName is not provided and there is only one sourceDescription', () => { + const ctx: TestContext = { + sourceDescriptions: [ + { + name: 'testApi', + type: 'openapi', + url: './openapi.yaml', + }, + ], + $sourceDescriptions: { + testApi: { + servers: [], + }, + }, + } as unknown as TestContext; + + const result = getServerUrl({ ctx, descriptionName: '' }); + expect(result).toBeUndefined(); + }); + + it('should return serverUrlOverride when ctx.sourceDescriptions has length 1 and contains x-serverUrl', () => { + const mockCtx = { + sourceDescriptions: [ + { + 'x-serverUrl': 'https://override.com', + }, + ], + } as unknown as TestContext; + + const mockDescriptionOperation = { + servers: [{ url: 'https://server1.com' }], + } as unknown as OperationDetails & { servers: { url: string }[] }; + const result = getServerUrl({ + ctx: mockCtx, + descriptionName: 'test', + openapiOperation: mockDescriptionOperation, + }); + + expect(result).toEqual({ url: 'https://override.com' }); + }); + + it('should return descriptionOperation.servers[0] when no serverUrlOverride is present', () => { + const mockDescriptionOperation = { + servers: [{ url: 'https://server1.com' }], + } as unknown as OperationDetails & { servers: { url: string }[] }; + const result = getServerUrl({ + ctx: { sourceDescriptions: [] } as unknown as TestContext, + descriptionName: 'test', + path: '', + openapiOperation: mockDescriptionOperation, + } as unknown as GetServerUrlInput); + + expect(result).toEqual({ url: 'https://server1.com' }); + }); + + it('should return undefined when descriptionOperation.servers is empty', () => { + const mockCtx = { + sourceDescriptions: [ + { + 'x-serverUrl': 'https://override.com', + }, + ], + } as unknown as TestContext; + + const result = getServerUrl({ + ctx: mockCtx, + descriptionName: 'test', + openapiOperation: { servers: [] } as unknown as OperationDetails & { + servers: { url: string }[]; + }, + }); + + expect(result).toBeUndefined(); + }); + + it('should return undefined when descriptionOperation is undefined', () => { + const mockCtx = { + sourceDescriptions: [ + { + 'x-serverUrl': 'https://override.com', + }, + ], + } as unknown as TestContext; + const result = getServerUrl({ + ctx: mockCtx, + descriptionName: 'test', + }); + + expect(result).toBeUndefined(); + }); + + it('should return serverUrlOverride when sourceDescriptionName is available and contains x-serverUrl', () => { + const mockCtx = { + sourceDescriptions: [ + { + name: 'test', + 'x-serverUrl': 'https://override1.com', + }, + { + name: 'test2', + 'x-serverUrl': 'https://override2.com', + }, + ], + } as unknown as TestContext; + + const mockDescriptionOperation = { + servers: [{ url: 'https://server1.com' }], + sourceDescriptionName: 'test2', + } as unknown as OperationDetails & { servers: { url: string }[] }; + const result = getServerUrl({ + ctx: mockCtx, + descriptionName: '', + openapiOperation: mockDescriptionOperation, + }); + + expect(result).toEqual({ url: 'https://override2.com' }); + }); + + it('should return server url from cli server option', () => { + const ctx = { + options: { server: 'test=https://cli.com' }, + } as unknown as TestContext; + const descriptionName = 'test'; + const result = getServerUrl({ ctx, descriptionName }); + expect(result).toEqual({ url: 'https://cli.com' }); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/get-workflows-to-run.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/get-workflows-to-run.test.ts new file mode 100644 index 0000000000..43b7523183 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/get-workflows-to-run.test.ts @@ -0,0 +1,95 @@ +import { getWorkflowsToRun } from '../../flow-runner'; +import { DefaultLogger } from '../../../utils/logger/logger'; + +const logger = DefaultLogger.getInstance(); + +describe('getWorkflowsToRun', () => { + const workflows = [ + { + workflowId: 'flow1', + steps: [], + }, + { + workflowId: 'flow2', + steps: [], + }, + ]; + it('should return all workflows if no workflowToRun and no workflowsToSkip are provided', () => { + const workflowToRun = undefined; + const workflowsToSkip = undefined; + const result = getWorkflowsToRun(workflows, workflowToRun, workflowsToSkip); + + expect(result).toEqual(workflows); + }); + + it('should return all workflows if workflowIds are empty', () => { + const workflowIds = [] as string[]; + const result = getWorkflowsToRun(workflows, workflowIds, undefined); + + expect(result).toEqual(workflows); + }); + + it('should return all workflows if workflowIds are not found', () => { + const workflowIds = ['flow3']; + + try { + getWorkflowsToRun(workflows, workflowIds, undefined); + } catch (error) { + expect(error.message).toEqual(`Following workflows don't exist: ${workflowIds.join(', ')}`); + } + }); + + it('should return only the workflows that match the workflowIds', () => { + const workflowIds = ['flow1']; + const result = getWorkflowsToRun(workflows, workflowIds, undefined); + + expect(result).toEqual([workflows[0]]); + }); + + it('should return only the workflows that match the workflowIds and ignore the one does not exist', () => { + const mockLogger = jest.spyOn(logger, 'log').mockImplementation(); + const workflowIds = ['flow1', 'flow2', 'flow3']; + const result = getWorkflowsToRun(workflows, workflowIds, undefined); + + expect(result).toEqual(workflows); + + expect(mockLogger).toHaveBeenCalledWith("Following workflows don't exist: flow3"); + + mockLogger.mockRestore(); + }); + + it('should return not skipped workflows when skip option is provided', () => { + const mockLogger = jest.spyOn(logger, 'log').mockImplementation(); + + const workflowToRun = undefined; + const workflowsToSkip = ['flow1']; + const result = getWorkflowsToRun(workflows, workflowToRun, workflowsToSkip); + + expect(mockLogger).toHaveBeenCalledTimes(1); + expect(mockLogger).toHaveBeenCalledWith( + expect.stringMatching('Following workflows are skipped: flow1') + ); + expect(result).toEqual([ + { + workflowId: 'flow2', + steps: [], + }, + ]); + + mockLogger.mockRestore(); + }); + + it('should return empty workflows list and write warning when all workflows are skipped', () => { + const mockLogger = jest.spyOn(logger, 'log').mockImplementation(); + + const workflowToRun = undefined; + const workflowsToSkip = ['flow1', 'flow2']; + const result = getWorkflowsToRun(workflows, workflowToRun, workflowsToSkip); + + expect(mockLogger).toHaveBeenCalledTimes(1); + expect(mockLogger).toHaveBeenCalledWith(expect.stringMatching('All workflows are skipped')); + expect(result).toEqual([]); + + mockLogger.mockRestore(); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/inputs/format-cli-inputs.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/inputs/format-cli-inputs.test.ts new file mode 100644 index 0000000000..172c825bc9 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/inputs/format-cli-inputs.test.ts @@ -0,0 +1,41 @@ +import { formatCliInputs } from '../../../flow-runner'; + +describe('formatCliInputs', () => { + it('should return empty object if input is undefined', () => { + expect(formatCliInputs(undefined)).toEqual({}); + }); + + it('should return empty object if input is empty string', () => { + expect(formatCliInputs('')).toEqual({}); + }); + + it('should return empty object if input is empty array', () => { + expect(formatCliInputs([])).toEqual({}); + }); + + it('should return object with key value pair if input is string', () => { + expect(formatCliInputs('key=value')).toEqual({ key: 'value' }); + }); + + it('should return object with key value pair if input is array with multiple elements', () => { + expect(formatCliInputs(['key1=value1', 'key2=value2'])).toEqual({ + key1: 'value1', + key2: 'value2', + }); + }); + + it('should return empty object when string without = sign provided', () => { + expect(formatCliInputs('test')).toEqual({}); + }); + + it('should return parsed object when stringify object provided', () => { + expect(formatCliInputs('{"firstname":"John", "lastname":"Wick"}')).toEqual({ + firstname: 'John', + lastname: 'Wick', + }); + }); + + it('should return object with key value pair if input is string with comma separated key value pairs', () => { + expect(formatCliInputs('key1=value1,key2=value2')).toEqual({ key1: 'value1', key2: 'value2' }); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/inputs/map-input-values-to-schema.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/inputs/map-input-values-to-schema.test.ts new file mode 100644 index 0000000000..a3c84bbe72 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/inputs/map-input-values-to-schema.test.ts @@ -0,0 +1,120 @@ +import { resolveInputValuesToSchema } from '../../../flow-runner'; + +describe('resolveInputValuesToSchema', () => { + it('should return empty object if values is empty', () => { + const schema = { + type: 'object', + properties: { + key: { + type: 'string', + }, + }, + }; + + expect(resolveInputValuesToSchema({}, schema)).toEqual({}); + }); + + it('should return empty object if schema is empty', () => { + const values = { + key: 'value', + }; + + expect(resolveInputValuesToSchema(values, {} as any)).toEqual({}); + }); + + it('should return empty object if values and schema are empty', () => { + expect(resolveInputValuesToSchema({}, {} as any)).toEqual({}); + }); + + it('should return mapped object if values and schema are provided', () => { + const values = { + key: 'value', + key2: 'value2', + }; + const schema = { + type: 'object', + properties: { + key: { + type: 'string', + }, + key2: { + type: 'string', + }, + }, + }; + + expect(resolveInputValuesToSchema(values, schema)).toEqual({ key: 'value', key2: 'value2' }); + }); + + it('should return only map values defined in the schema', () => { + const values = { + key: 'value', + key2: 'value2', + }; + const schema = { + type: 'object', + properties: { + key: { + type: 'string', + }, + }, + }; + + expect(resolveInputValuesToSchema(values, schema)).toEqual({ key: 'value' }); + }); + + it('should return mapped nested values defined in the schema', () => { + const values = { + username: 'value', + password: 'value2', + info: { + home: 'home', + work: 'work', + car: { + type: 'heavy', + brand: 'some', + minSpeed: 0, + }, + }, + }; + const schema = { + type: 'object', + properties: { + username: { + type: 'string', + }, + password: { + type: 'string', + }, + info: { + type: 'object', + properties: { + home: { + type: 'string', + }, + work: { + type: 'string', + }, + car: { + type: 'object', + properties: { + type: { + type: 'string', + }, + maxSpeed: { + type: 'number', + }, + }, + }, + }, + }, + }, + }; + + expect(resolveInputValuesToSchema(values, schema)).toEqual({ + username: 'value', + password: 'value2', + info: { home: 'home', work: 'work', car: { type: 'heavy' } }, + }); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/outputs/print-message.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/outputs/print-message.test.ts new file mode 100644 index 0000000000..f99f327e2b --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/outputs/print-message.test.ts @@ -0,0 +1,22 @@ +import { printExpected, printReceived } from '../../../flow-runner'; + +describe('printExpected', () => { + it('should print expected', () => { + // check encoded color of the text + expect(printExpected('expected')).toBe('"expected"'); + }); + + it('should print expected', () => { + expect(printExpected({})).toBe('{}'); + }); +}); + +describe('printReceived', () => { + it('should print received', () => { + expect(printReceived('received')).toBe('"received"'); + }); + + it('should print received', () => { + expect(printReceived({})).toBe('{}'); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/prepare-request.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/prepare-request.test.ts new file mode 100644 index 0000000000..6e9e83683d --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/prepare-request.test.ts @@ -0,0 +1,1501 @@ +import type { TestContext, Step } from '../../../types'; + +import { prepareRequest } from '../../flow-runner'; +import { ApiFetcher } from '../../../utils/api-fetcher'; + +describe('prepareRequest', () => { + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const ctx = { + $env: { + REDOCLY_DOMAIN: 'redocly.com', + }, + $faker: { + address: {}, + date: {}, + number: {}, + string: {}, + }, + $sourceDescriptions: { + cats: { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/fact': { + get: { + tags: ['Facts'], + summary: 'Get Random Fact', + description: 'Returns a random fact', + operationId: 'getRandomFact', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + '404': { + description: 'Fact not found', + }, + }, + }, + }, + '/facts': { + get: { + tags: ['Facts'], + summary: 'Get a list of facts', + description: 'Returns a a list of facts', + operationId: 'getFacts', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [ + { + url: 'https://catfact.ninja/', + }, + ], + info: { + title: 'Cat Facts API', + version: '1.0', + }, + }, + }, + workflows: [ + { + workflowId: 'get-breeds-workflow', + steps: [ + { + stepId: 'get-breeds-step', + operationId: 'cats.getBreeds', + checks: [], + response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Aegean', + country: 'Greece', + origin: 'Natural/Standard', + coat: 'Semi-long', + pattern: 'Bi- or tri-colored', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + code: 200, + headers: {}, + contentType: 'application/json', + }, + verboseLog: {}, + }, + ], + }, + ], + $workflows: { + 'get-breeds-workflow': { + steps: { + 'get-breeds-step': { + request: { + headers: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + }, + }, + }, + }, + $steps: { + 'get-breeds-step': { + request: { + headers: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + }, + }, + harLogs: {}, + options: { + workflowPath: 'simple.yaml', + harLogsFile: 'har-output', + metadata: { + _: [], + files: ['simple.yaml'], + $0: 'respect', + file: 'simple.yaml', + }, + }, + 'x-serverUrl': '', + info: { + title: 'Cat Facts API', + version: '1.0', + }, + arazzo: '1.0.1', + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + $outputs: {}, + apiClient, + } as unknown as TestContext; + const workflowName = 'get-breeds-workflow'; + const step = ctx.workflows[0].steps[0]; + + it('should set apiClient step params', async () => { + const { path, method, parameters, requestBody, openapiOperation, serverUrl } = + await prepareRequest(ctx, step, workflowName); + + expect(path).toEqual('/breeds'); + expect(method).toEqual('get'); + expect(parameters).toEqual([ + { + value: 'application/json', + in: 'header', + name: 'accept', + }, + ]); + expect(openapiOperation).toEqual({ + description: 'Returns a a list of breeds', + descriptionName: 'cats', + method: 'get', + operationId: 'getBreeds', + parameters: [ + { + description: 'limit the amount of results returned', + in: 'query', + name: 'limit', + required: false, + schema: { + format: 'int64', + type: 'integer', + }, + }, + ], + path: '/breeds', + pathParameters: [ + { + description: 'limit the amount of results returned', + in: 'query', + name: 'limit', + required: false, + schema: { + format: 'int64', + type: 'integer', + }, + }, + ], + responses: { + '200': { + content: { + 'application/json': { + schema: { + items: { + description: 'Breed', + properties: { + breed: { + description: 'Breed', + format: 'string', + title: 'Breed', + type: 'string', + }, + coat: { + description: 'Coat', + format: 'string', + title: 'Coat', + type: 'string', + }, + country: { + description: 'Country', + format: 'string', + title: 'Country', + type: 'string', + }, + origin: { + description: 'Origin', + format: 'string', + title: 'Origin', + type: 'string', + }, + pattern: { + description: 'Pattern', + format: 'string', + title: 'Pattern', + type: 'string', + }, + }, + title: 'Breed model', + type: 'object', + }, + type: 'array', + }, + }, + }, + description: 'successful operation', + }, + }, + servers: [{ url: 'https://catfact.ninja/' }], + summary: 'Get a list of breeds', + tags: ['Breeds'], + }); + expect(serverUrl).toEqual({ url: 'https://catfact.ninja/' }); + expect(requestBody).toEqual(undefined); + }); + + it('should set apiClient step params when descriptionOperation not provided', async () => { + const step = { + stepId: 'get-breeds-step', + 'x-operation': { + url: 'http://localhost:3000/breeds', + method: 'get', + }, + response: {}, + checks: [], + } as unknown as Step; + + const ctx = { + apiClient, + $env: { + REDOCLY_DOMAIN: 'redocly.com', + }, + $faker: { + address: {}, + date: {}, + number: {}, + string: {}, + }, + $descriptions: {}, + workflows: [ + { + workflowId: 'get-breeds-workflow', + steps: [ + { + stepId: 'get-breeds-step', + 'x-operation': { + url: 'http://localhost:3000/breeds', + method: 'get', + }, + checks: [], + response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Aegean', + country: 'Greece', + origin: 'Natural/Standard', + coat: 'Semi-long', + pattern: 'Bi- or tri-colored', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + code: 200, + headers: {}, + contentType: 'application/json', + }, + verboseLog: {}, + }, + ], + }, + ], + $workflows: { + 'get-breeds-workflow': { + steps: { + 'get-breeds-step': { + request: { + headers: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + }, + }, + }, + }, + $steps: { + 'get-breeds-step': { + request: { + headers: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + }, + }, + harLogs: {}, + options: { + workflowPath: 'simple.yaml', + harLogsFile: 'har-output', + metadata: { + _: [], + files: ['simple.yaml'], + $0: 'respect', + file: 'simple.yaml', + }, + }, + 'x-serverUrl': 'https://catfact.ninja/', + info: { + title: 'Cat Facts API', + version: '1.0', + }, + arazzo: '1.0.1', + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + $outputs: {}, + } as unknown as TestContext; + + const { path, method, openapiOperation } = await prepareRequest(ctx, step, workflowName); + + expect(path).toEqual(''); + expect(method).toEqual('get'); + expect(openapiOperation).toEqual(undefined); + }); + + it('should set $steps and steps in $workflows items to ctx', async () => { + const step = { + stepId: 'get-breeds-step', + 'x-operation': { + url: 'http://localhost:3000/breeds', + method: 'get', + }, + response: {}, + checks: [], + } as unknown as Step; + + const ctx = { + apiClient, + $env: { + REDOCLY_DOMAIN: 'redocly.com', + }, + $faker: { + address: {}, + date: {}, + number: {}, + string: {}, + }, + $descriptions: {}, + workflows: [ + { + workflowId: 'get-breeds-workflow', + steps: [ + { + stepId: 'get-breeds-step', + operationId: 'cats.getBreeds', + checks: [], + response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Aegean', + country: 'Greece', + origin: 'Natural/Standard', + coat: 'Semi-long', + pattern: 'Bi- or tri-colored', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + code: 200, + headers: {}, + contentType: 'application/json', + }, + verboseLog: {}, + }, + ], + }, + ], + $workflows: { + 'get-breeds-workflow': { + steps: { + 'get-breeds-step': { + request: {}, + response: {}, + outputs: {}, + }, + }, + }, + }, + $steps: {}, + harLogs: {}, + options: { + workflowPath: 'simple.yaml', + harLogsFile: 'har-output', + metadata: { + _: [], + files: ['simple.yaml'], + $0: 'respect', + file: 'simple.yaml', + }, + }, + info: { + title: 'Cat Facts API', + version: '1.0', + }, + arazzo: '1.0.1', + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + $outputs: {}, + } as unknown as TestContext; + + const { path, method, openapiOperation } = await prepareRequest(ctx, step, workflowName); + + expect(path).toEqual(''); + expect(method).toEqual('get'); + expect(openapiOperation).toEqual(undefined); + }); + + it('should throw an error when serverUrl not defined', async () => { + const step = { + stepId: 'get-breeds-step', + operationId: 'cats.getBreeds', + response: {}, + checks: [], + } as unknown as Step; + + const ctx = { + apiClient, + $env: { + REDOCLY_DOMAIN: 'redocly.com', + }, + $faker: { + address: {}, + date: {}, + number: {}, + string: {}, + }, + $descriptions: { + cats: { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [], + info: { + title: 'Cat Facts API', + version: '1.0', + }, + }, + }, + workflows: [ + { + workflowId: 'get-breeds-workflow', + steps: [ + { + stepId: 'get-breeds-step', + operationId: 'cats.getBreeds', + checks: [], + response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Aegean', + country: 'Greece', + origin: 'Natural/Standard', + coat: 'Semi-long', + pattern: 'Bi- or tri-colored', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + code: 200, + headers: {}, + contentType: 'application/json', + }, + verboseLog: {}, + }, + ], + }, + ], + $workflows: { + 'get-breeds-workflow': { + steps: { + 'get-breeds-step': { + request: {}, + response: {}, + outputs: {}, + }, + }, + }, + }, + $steps: {}, + harLogs: {}, + options: { + workflowPath: 'simple.yaml', + harLogsFile: 'har-output', + metadata: { + _: [], + files: ['simple.yaml'], + $0: 'respect', + file: 'simple.yaml', + }, + }, + 'x-serverUrl': '', + info: { + title: 'Cat Facts API', + version: '1.0', + }, + arazzo: '1.0.1', + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + { + name: 'dogs', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + $sourceDescriptions: { + cats: { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [], + info: { + title: 'Cat Facts API', + version: '1.0', + }, + }, + }, + $outputs: {}, + } as unknown as TestContext; + + await expect(prepareRequest(ctx, step, workflowName)).rejects.toThrow( + 'No servers found in API description' + ); + }); + + it('should through an error when method missing', async () => { + const step = { + stepId: 'get-breeds-step', + 'x-operation': { + url: 'https://catfact.ninja/breeds', + method: '', + }, + response: {}, + checks: [], + } as unknown as Step; + + const ctx = { + apiClient, + $env: { + REDOCLY_DOMAIN: 'redocly.com', + }, + $faker: { + address: {}, + date: {}, + number: {}, + string: {}, + }, + $descriptions: {}, + workflows: [ + { + workflowId: 'get-breeds-workflow', + steps: [ + { + stepId: 'get-breeds-step', + operationId: 'cats.getBreeds', + checks: [], + response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Aegean', + country: 'Greece', + origin: 'Natural/Standard', + coat: 'Semi-long', + pattern: 'Bi- or tri-colored', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + code: 200, + headers: {}, + contentType: 'application/json', + }, + verboseLog: {}, + }, + ], + }, + ], + $workflows: { + 'get-breeds-workflow': { + steps: {}, + }, + }, + $steps: {}, + harLogs: {}, + options: { + workflowPath: 'simple.yaml', + harLogsFile: 'har-output', + metadata: { + _: [], + files: ['simple.yaml'], + $0: 'respect', + file: 'simple.yaml', + }, + }, + 'x-serverUrl': '', + info: { + title: 'Cat Facts API', + version: '1.0', + }, + arazzo: '1.0.1', + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + { + name: 'cats-legacy', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + $outputs: {}, + } as unknown as TestContext; + + try { + await prepareRequest(ctx, step, workflowName); + } catch (e) { + expect(e.message).toEqual('"method" is required to make a request'); + } + }); + + it('should merge workflow and step level params with reference property without duplicates prefer step params', async () => { + const localCtx = { + ...ctx, + ...{ + workflows: [ + { + workflowId: 'get-breeds-workflow', + parameters: [ + { + reference: '$components.parameters.page', + value: 100, + }, + ], + steps: [ + { + stepId: 'get-breeds-step', + operationId: 'cats.getBreeds', + checks: [], + response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Aegean', + country: 'Greece', + origin: 'Natural/Standard', + coat: 'Semi-long', + pattern: 'Bi- or tri-colored', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + code: 200, + headers: {}, + contentType: 'application/json', + }, + verboseLog: {}, + }, + ], + }, + ], + $workflows: { + 'get-breeds-workflow': { + parameters: [ + { + reference: '$components.parameters.page', + value: 100, + }, + ], + steps: { + 'get-breeds-step': { + request: { + headers: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + }, + }, + }, + }, + $components: { + parameters: { + page: { + name: 'page', + in: 'header', + value: 1, + }, + pageSize: { + name: 'pageSize', + in: 'header', + value: 100, + }, + }, + }, + }, + } as unknown as TestContext; + + const step = { + stepId: 'get-breeds-step', + operationId: 'cats.getBreeds', + checks: [], + response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Aegean', + country: 'Greece', + origin: 'Natural/Standard', + coat: 'Semi-long', + pattern: 'Bi- or tri-colored', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + code: 200, + headers: {}, + contentType: 'application/json', + }, + verboseLog: {}, + parameters: [ + { + reference: '$components.parameters.page', + }, + ], + } as unknown as Step; + + const { path, method, parameters } = await prepareRequest(localCtx, step, workflowName); + + expect(path).toEqual('/breeds'); + expect(method).toEqual('get'); + expect(parameters).toEqual([ + { + value: 'application/json', + in: 'header', + name: 'accept', + }, + { + value: 1, + in: 'header', + name: 'page', + }, + ]); + }); + + it('should merge workflow and step level unique reference params', async () => { + const localCtx = { + ...ctx, + ...{ + workflows: [ + { + workflowId: 'get-breeds-workflow', + parameters: [ + { + name: 'pageSize', + in: 'header', + value: 100, + }, + ], + steps: [ + { + stepId: 'get-breeds-step', + operationId: 'cats.getBreeds', + checks: [], + response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Aegean', + country: 'Greece', + origin: 'Natural/Standard', + coat: 'Semi-long', + pattern: 'Bi- or tri-colored', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + code: 200, + headers: {}, + contentType: 'application/json', + }, + verboseLog: {}, + }, + ], + }, + ], + $workflows: { + 'get-breeds-workflow': { + parameters: [ + { + name: 'pageSize', + in: 'header', + value: 100, + }, + ], + steps: { + 'get-breeds-step': { + request: { + headers: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + }, + }, + }, + }, + $components: { + parameters: { + page: { + name: 'page', + in: 'header', + value: 1, + }, + pageSize: { + name: 'pageSize', + in: 'header', + value: 100, + }, + }, + }, + }, + } as unknown as TestContext; + + const step = { + stepId: 'get-breeds-step', + operationId: 'cats.getBreeds', + checks: [], + response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Aegean', + country: 'Greece', + origin: 'Natural/Standard', + coat: 'Semi-long', + pattern: 'Bi- or tri-colored', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + code: 200, + headers: {}, + contentType: 'application/json', + }, + verboseLog: {}, + parameters: [ + { + reference: '$components.parameters.page', + }, + ], + } as unknown as Step; + + const { path, method, parameters } = await prepareRequest(localCtx, step, workflowName); + + expect(path).toEqual('/breeds'); + expect(method).toEqual('get'); + expect(parameters).toEqual([ + { + value: 'application/json', + in: 'header', + name: 'accept', + }, + { + value: 100, + in: 'header', + name: 'pageSize', + }, + { + value: 1, + in: 'header', + name: 'page', + }, + ]); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/read-env-variables.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/read-env-variables.test.ts new file mode 100644 index 0000000000..5ab70a4694 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/read-env-variables.test.ts @@ -0,0 +1,60 @@ +import * as dotenv from 'dotenv'; +import * as fs from 'fs'; +import * as path from 'path'; + +import { readEnvVariables } from '../../flow-runner'; + +jest.mock('dotenv'); +jest.mock('fs'); + +describe('readEnvVariables', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should load environment variables from the correct .env file', () => { + const mockExecutionFilePath = '/some/folder/executionFile.js'; + const mockEnvPath = path.resolve(path.dirname(mockExecutionFilePath), '.env'); + + (fs.existsSync as jest.Mock).mockReturnValue(true); + + // Mock dotenv.config to simulate loading of environment variables + (dotenv.config as jest.Mock).mockImplementation(({ path }) => { + if (path === mockEnvPath) { + process.env.MOCK_VAR = 'mock_value'; + return { parsed: { MOCK_VAR: 'mock_value' } }; + } + return { parsed: undefined }; + }); + + const result = readEnvVariables(mockExecutionFilePath); + + expect(fs.existsSync).toHaveBeenCalledWith(mockEnvPath); + expect(dotenv.config).toHaveBeenCalledWith({ path: mockEnvPath }); + // @ts-ignore + expect(result?.MOCK_VAR).toBe('mock_value'); + + // Cleanup + delete process.env.MOCK_VAR; + }); + + it('should return process.env when .env file does not exist', () => { + const mockExecutionFilePath = '/some/folder/executionFile.js'; + + (fs.existsSync as jest.Mock).mockReturnValue(false); + + const result = readEnvVariables(mockExecutionFilePath); + + expect(fs.existsSync).toHaveBeenCalled(); + expect(dotenv.config).not.toHaveBeenCalled(); + expect(result).toBe(process.env); + }); + + it('should return process.env when no executionFilePath is provided', () => { + const result = readEnvVariables(); + + expect(fs.existsSync).not.toHaveBeenCalled(); + expect(dotenv.config).not.toHaveBeenCalled(); + expect(result).toBe(process.env); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/resolve-running-workflows.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/resolve-running-workflows.test.ts new file mode 100644 index 0000000000..3a35f33212 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/resolve-running-workflows.test.ts @@ -0,0 +1,47 @@ +import { resolveRunningWorkflows } from '../../flow-runner'; + +describe('resolveRunningWorkflows', () => { + it('should return undefined if no workflows are provided', () => { + expect(resolveRunningWorkflows(undefined)).toBeUndefined(); + }); + + it('should return an array of workflows if a single workflow is provided', () => { + expect(resolveRunningWorkflows('workflow1')).toEqual(['workflow1']); + }); + + it('should return an array of workflows if multiple workflows are provided', () => { + expect(resolveRunningWorkflows(['workflow1', 'workflow2'])).toEqual(['workflow1', 'workflow2']); + }); + + it('should return an array of workflows if multiple workflows are provided with commas', () => { + expect(resolveRunningWorkflows('workflow1,workflow2')).toEqual(['workflow1', 'workflow2']); + }); + + it('should return an array of workflows if multiple workflows are provided with commas in array', () => { + expect(resolveRunningWorkflows(['workflow1,workflow2', 'workflow3'])).toEqual([ + 'workflow1', + 'workflow2', + 'workflow3', + ]); + }); + + it('should trim whitespace from workflow names when splitting by commas', () => { + expect(resolveRunningWorkflows('workflow1, workflow2 , workflow3')).toEqual([ + 'workflow1', + 'workflow2', + 'workflow3', + ]); + }); + + it('should return an array of workflows if multiple workflows are provided with commas in array and some workflows are skipped', () => { + expect(resolveRunningWorkflows(['workflow1,workflow2', 'workflow3'])).toEqual([ + 'workflow1', + 'workflow2', + 'workflow3', + ]); + }); + + it('should return undefined if workflows is not a string or array', () => { + expect(resolveRunningWorkflows(1 as any)).toBeUndefined(); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/run-step.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/run-step.test.ts new file mode 100644 index 0000000000..fca2de7ef1 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/run-step.test.ts @@ -0,0 +1,4047 @@ +import type { Step, TestContext, ResponseContext } from '../../../types'; + +import { + runStep, + callAPIAndAnalyzeResults, + checkCriteria, + runWorkflow, + resolveWorkflowContext, + DEFAULT_SEVERITY_CONFIGURATION, + CHECKS, +} from '../../flow-runner'; +import { createHarLog } from '../../../utils/har-logs'; +import { ApiFetcher } from '../../../utils/api-fetcher'; +import { displayChecks } from '../../cli-output'; +import { cleanColors } from '../../../utils/clean-colors'; + +jest.mock('../../flow-runner/call-api-and-analyze-results', () => ({ + callAPIAndAnalyzeResults: jest.fn(), +})); + +jest.mock('../../cli-output', () => ({ + displayChecks: jest.fn(), +})); + +jest.mock('../../flow-runner/success-criteria', () => ({ + checkCriteria: jest.fn(), +})); + +jest.mock('../../flow-runner/runner', () => ({ + runWorkflow: jest.fn(), + resolveWorkflowContext: jest.fn(), +})); + +const harLogs = createHarLog(); +const apiClient = new ApiFetcher({ + harLogs, +}); +const basicCTX = { + apiClient, + $request: undefined, + $response: undefined, + $env: {}, + $faker: { + address: {}, + date: {}, + number: {}, + string: {}, + }, + severity: DEFAULT_SEVERITY_CONFIGURATION, + $sourceDescriptions: { + 'reusable-api': { + arazzo: '1.0.1', + info: { + title: 'Reusable API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + workflows: [ + { + workflowId: 'reusable-get-bird-workflow', + parameters: [ + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + steps: [ + { + stepId: 'reusable-first-step-one', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + { + stepId: 'reusable-delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + ], + }, + { + workflowId: 'reusable-get-bird-workflow-2', + parameters: [ + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + steps: [ + { + stepId: 'reusable-first-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + { + stepId: 'reusable-delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + ], + outputs: { + wowStatusCode: '$steps.reusable-delete-step.response.statusCode', + }, + }, + { + workflowId: 'second-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + dependsOn: ['reusable-get-bird-workflow', 'reusable-get-bird-workflow-2'], + steps: [ + { + stepId: 'delete-small-mock', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + onSuccess: [ + { + name: 'testRetry', + type: 'goto', + workflowId: 'reusable-get-bird-workflow', + }, + ], + }, + ], + }, + ], + }, + cats: { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/fact': { + get: { + tags: ['Facts'], + summary: 'Get Random Fact', + description: 'Returns a random fact', + operationId: 'getRandomFact', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + '404': { + description: 'Fact not found', + }, + }, + }, + }, + '/facts': { + get: { + tags: ['Facts'], + summary: 'Get a list of facts', + description: 'Returns a a list of facts', + operationId: 'getFacts', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [ + { + url: 'https://catfact.ninja/', + }, + ], + info: { + title: 'Cat Facts API', + version: '1.0', + }, + }, + 'no-serverUrl-api': { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/fact': { + get: { + tags: ['Facts'], + summary: 'Get Random Fact', + description: 'Returns a random fact', + operationId: 'getRandomFact', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + '404': { + description: 'Fact not found', + }, + }, + }, + }, + '/facts': { + get: { + tags: ['Facts'], + summary: 'Get a list of facts', + description: 'Returns a a list of facts', + operationId: 'getFacts', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [], + info: { + title: 'Cat Facts API', + version: '1.0', + }, + }, + }, + sourceDescriptions: [ + { name: 'cats', type: 'openapi', url: 'api-samples/cats.yaml' }, + { name: 'reusable-api', type: 'arazzo', url: 'small.yml' }, + { name: 'no-serverUrl-api', type: 'arazzo', url: 'no-serverUrl-api.yml' }, + ], + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { url: 'http://localhost:3000/bird', method: 'get' }, + successCriteria: [{ condition: '$statusCode == 200' }], + checks: [], + }, + ], + }, + ], + $workflows: { + 'get-bird-workflow': { + steps: { + 'get-bird': {}, + }, + }, + }, + $steps: {}, + harLogs: {}, + options: { + workflowPath: 'runStepTest.yml', + workflow: undefined, + skip: undefined, + verbose: true, + harLogsFile: 'har-output', + metadata: { + _: [], + files: ['runStepTest.yml'], + $0: 'respect', + file: 'runStepTest.yml', + }, + input: undefined, + }, + info: { title: 'Test API', version: '1.0' }, + arazzo: '1.0.1', + $outputs: {}, +} as unknown as TestContext; +const testDocumentFilePath = 'test.yml'; + +describe('runStep', () => { + afterEach(() => { + jest.resetAllMocks(); + jest.clearAllMocks(); + }); + + it('should run step and display checks', async () => { + const step = { + stepId: 'get-bird', + 'x-operation': { url: 'http://localhost:3000/bird', method: 'get' }, + successCriteria: [{ condition: '$statusCode == 200' }], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + // @ts-ignore + callAPIAndAnalyzeResults.mockImplementationOnce(async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + message: '', + severity: 'error', + }, + { + name: CHECKS.CONTENT_TYPE_CHECK, + pass: true, + message: '', + severity: 'error', + }, + ]; + + return { successCriteriaCheck: true, expectCheck: true }; + }); + + await runStep({ + step, + ctx: basicCTX, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(displayChecks).toMatchSnapshot(); + }); + + it('should run step and return failed result when workflowName is missing', async () => { + const step = { + stepId: 'get-bird', + 'x-operation': { url: 'http://localhost:3000/bird', method: 'get' }, + successCriteria: [{ condition: '$statusCode == 200' }], + checks: [], + } as unknown as Step; + const workflowName = undefined; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + await runStep({ + step, + ctx: basicCTX, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(step.checks).toEqual([ + { + message: 'Workflow name is required to run a step', + name: CHECKS.UNEXPECTED_ERROR, + pass: false, + severity: 'error', + }, + ]); + }); + + it('should display checks when failed to make a call to the API', async () => { + const apiClient = new ApiFetcher({ + harLogs, + }); + const step = { + stepId: 'get-bird', + operationId: 'no-serverUrl-api.getBreeds', + successCriteria: [{ condition: '$statusCode == 200' }], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + // @ts-ignore + callAPIAndAnalyzeResults.mockImplementationOnce(async ({ step }: { step: Step }) => { + step.checks = []; + + return { successCriteriaCheck: true, expectCheck: true }; + }); + await runStep({ + step, + ctx: basicCTX, + workflowName, + parentStepId, + parentWorkflowId, + }); + + // @ts-ignore + expect(step.checks).toEqual([ + { + message: 'No servers found in API description', + name: CHECKS.UNEXPECTED_ERROR, + pass: false, + severity: 'error', + }, + ]); + }); + + it('should execute onSuccess step criteria with goto StepId', async () => { + const apiClient = new ApiFetcher({ + harLogs, + }); + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + onSuccess: [ + { + name: 'success-action', + stepId: 'success-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + // @ts-ignore + callAPIAndAnalyzeResults.mockImplementationOnce(async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + message: '', + severity: 'error', + }, + { + name: CHECKS.CONTENT_TYPE_CHECK, + pass: true, + message: '', + severity: 'error', + }, + ]; + + return { successCriteriaCheck: true, expectCheck: true }; + }); + + // @ts-ignore + checkCriteria.mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + severity: 'error', + }, + ]); + + const context = { + ...basicCTX, + apiClient, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onSuccess: [ + { + name: 'success-action', + stepId: 'success-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + }, + { + stepId: 'success-action-step', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + }, + } as unknown as TestContext; + + // @ts-ignore + resolveWorkflowContext.mockImplementationOnce(() => { + return { ...context }; + }); + + await runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(displayChecks).toHaveBeenCalled(); + expect(checkCriteria).toHaveBeenCalled(); + }); + + it('should execute onSuccess step criteria with goto StepId provided by reference', async () => { + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + onSuccess: [ + { + reference: '$components.successActions.success-action', + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + // @ts-ignore + callAPIAndAnalyzeResults.mockImplementationOnce(async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + message: '', + severity: 'error', + }, + { + name: CHECKS.CONTENT_TYPE_CHECK, + pass: true, + message: '', + severity: 'error', + }, + ]; + + return { successCriteriaCheck: true, expectCheck: true }; + }); + + // @ts-ignore + checkCriteria.mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + }, + ]); + + const context = { + ...basicCTX, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onSuccess: [ + { + name: 'success-action', + stepId: 'success-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + }, + { + stepId: 'success-action-step', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + $components: { + successActions: { + 'success-action': { + name: 'success-action', + stepId: 'success-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + }, + }, + }, + } as unknown as TestContext; + + // @ts-ignore + resolveWorkflowContext.mockImplementationOnce(() => { + return { ...context }; + }); + + await runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(displayChecks).toHaveBeenCalled(); + expect(checkCriteria).toHaveBeenCalled(); + }); + + it('should execute onSuccess step criteria with goto workflowId', async () => { + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + onSuccess: [ + { + name: 'success-action', + workflowId: 'success-action-workflow', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + (callAPIAndAnalyzeResults as jest.Mock).mockImplementationOnce( + async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + message: '', + severity: 'error', + }, + { + name: CHECKS.CONTENT_TYPE_CHECK, + pass: true, + message: '', + severity: 'error', + }, + ]; + + return { successCriteriaCheck: true, expectCheck: true }; + } + ); + + (checkCriteria as jest.Mock).mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + }, + ]); + + const context = { + ...basicCTX, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onSuccess: [ + { + name: 'success-action', + stepId: 'success-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + }, + { + stepId: 'success-action-step', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + }, + } as unknown as TestContext; + + // @ts-ignore + runWorkflow.mockImplementationOnce(() => { + return () => {}; + }); + + // @ts-ignore + resolveWorkflowContext.mockImplementationOnce(() => { + return { ...context }; + }); + + await runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(displayChecks).toHaveBeenCalled(); + expect(checkCriteria).toHaveBeenCalled(); + expect(runWorkflow).toHaveBeenCalled(); + }); + + it('should log error when onSuccess step criteria with goto StepId and WorkflowId provided', async () => { + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + onSuccess: [ + { + name: 'success-action', + stepId: 'success-action-step', + workflowId: 'success-action-workflow', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + (callAPIAndAnalyzeResults as jest.Mock).mockImplementationOnce( + async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + message: '', + severity: 'error', + }, + { + name: 'MIME-TYPE CHECK', + pass: true, + message: '', + severity: 'error', + }, + ]; + + return { successCriteriaCheck: true, expectCheck: true }; + } + ); + + (checkCriteria as jest.Mock).mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + }, + ]); + + const context = { + ...basicCTX, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onSuccess: [ + { + name: 'success-action', + stepId: 'success-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + }, + { + stepId: 'success-action-step', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + }, + } as unknown as TestContext; + + // @ts-ignore + resolveWorkflowContext.mockImplementationOnce(() => { + return { ...context }; + }); + + await expect( + runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }) + ).rejects.toThrowError( + 'Cannot use both workflowId: success-action-workflow and stepId: success-action-step in goto action' + ); + + expect(displayChecks).toHaveBeenCalled(); + expect(runWorkflow).not.toHaveBeenCalled(); + }); + + it('should execute onFailure step criteria with goto StepId', async () => { + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + // @ts-ignore + (callAPIAndAnalyzeResults as jest.Mock).mockImplementationOnce( + async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + { + name: CHECKS.CONTENT_TYPE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + ]; + + return { successCriteriaCheck: false, expectCheck: true }; + } + ); + + (checkCriteria as jest.Mock).mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: false, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + severity: 'error', + }, + ]); + + const context = { + ...basicCTX, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + }, + { + stepId: 'failure-action-step', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + }, + } as unknown as TestContext; + + // @ts-ignore + resolveWorkflowContext.mockImplementationOnce(() => { + return { ...context }; + }); + + await runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(displayChecks).toHaveBeenCalled(); + expect(checkCriteria).toHaveBeenCalled(); + }); + + it('should execute onFailure step criteria with goto StepId provided by reference', async () => { + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + onFailure: [ + { + reference: '$components.failureActions.failure-action', + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + // @ts-ignore + (callAPIAndAnalyzeResults as jest.Mock).mockImplementationOnce( + async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + { + name: CHECKS.CONTENT_TYPE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + ]; + + return { successCriteriaCheck: false, expectCheck: true }; + } + ); + + (checkCriteria as jest.Mock).mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: false, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + severity: 'error', + }, + ]); + + const context = { + ...basicCTX, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onFailure: [ + { + name: 'failure-action', + stepId: 'failure-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + }, + { + stepId: 'failure-action-step', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + $components: { + failureActions: { + 'failure-action': { + name: 'failure-action', + stepId: 'failure-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + }, + }, + }, + } as unknown as TestContext; + + // @ts-ignore + resolveWorkflowContext.mockImplementationOnce(() => { + return { ...context }; + }); + + await runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(displayChecks).toHaveBeenCalled(); + expect(checkCriteria).toHaveBeenCalled(); + }); + + it('should execute onFailure step criteria with retry StepId', async () => { + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + type: 'retry', + retryAfter: 1000, + retryLimit: 2, + criteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + (callAPIAndAnalyzeResults as jest.Mock).mockImplementation(async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + { + name: CHECKS.CONTENT_TYPE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + ]; + + return { successCriteriaCheck: false, expectCheck: true }; + }); + + (checkCriteria as jest.Mock).mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + severity: 'error', + }, + ]); + + const context = { + ...basicCTX, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + }, + { + stepId: 'failure-action-step', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + }, + } as unknown as TestContext; + + // @ts-ignore + resolveWorkflowContext.mockImplementationOnce(() => { + return { ...context }; + }); + + await runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(displayChecks).toHaveBeenCalled(); + expect(checkCriteria).toHaveBeenCalledTimes(8); + }); + + it('should result with an error when onFailure step criteria with retry StepId and WorkflowId provided', async () => { + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + workflowId: 'failure-action-workflow', + type: 'retry', + retryAfter: 1000, + retryLimit: 2, + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + // @ts-ignore + (callAPIAndAnalyzeResults as jest.Mock).mockImplementation(async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + { + name: CHECKS.CONTENT_TYPE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + ]; + + return { successCriteriaCheck: false, expectCheck: true }; + }); + + // @ts-ignore + (checkCriteria as jest.Mock).mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + severity: 'error', + }, + ]); + + const context = { + ...basicCTX, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + }, + { + stepId: 'failure-action-step', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + }, + } as unknown as TestContext; + + expect( + async () => + await runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }) + ).rejects.toThrow( + 'Cannot use both workflowId: failure-action-workflow and stepId: failure-action-step in retry action' + ); + }); + + it('should execute onFailure step criteria with retry when StepId with additional criteria check', async () => { + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + type: 'retry', + retryAfter: 1000, + retryLimit: 2, + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + (callAPIAndAnalyzeResults as jest.Mock).mockImplementation(async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + { + name: CHECKS.CONTENT_TYPE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + ]; + + if (step.stepId === 'get-bird') { + step.response = { + body: { + bird: '🐦', + name: 'hawk', + }, + statusCode: 200, + headers: new Headers(), + contentType: 'application/json', + } as unknown as ResponseContext; + } + + return { successCriteriaCheck: false, expectCheck: true }; + }); + + // @ts-ignore + (checkCriteria as jest.Mock).mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + severity: 'error', + }, + ]); + + const context = { + ...basicCTX, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + }, + { + stepId: 'failure-action-step', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + }, + } as unknown as TestContext; + + // @ts-ignore + resolveWorkflowContext.mockImplementation(() => { + return { ...context }; + }); + + await runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(displayChecks).toHaveBeenCalled(); + expect(checkCriteria).toHaveBeenCalledTimes(8); + }); + + it('should execute onFailure step criteria with successful retry', async () => { + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + type: 'retry', + retryAfter: 1000, + retryLimit: 2, + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + // @ts-ignore + (callAPIAndAnalyzeResults as jest.Mock).mockImplementationOnce( + async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + ]; + + if (step.stepId === 'get-bird') { + step.response = { + body: { + bird: '🐦', + name: 'hawk', + }, + statusCode: 200, + headers: new Headers(), + contentType: 'application/json', + } as unknown as ResponseContext; + } + + return { successCriteriaCheck: false, expectCheck: true }; + } + ); + + (callAPIAndAnalyzeResults as jest.Mock).mockImplementationOnce( + async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + message: '', + severity: 'error', + }, + ]; + + return { successCriteriaCheck: true, expectCheck: true }; + } + ); + + (callAPIAndAnalyzeResults as jest.Mock).mockImplementationOnce( + async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + message: '', + severity: 'error', + }, + ]; + + if (step.stepId === 'get-bird') { + step.response = { + body: { + bird: '🐦', + name: 'hawk', + }, + statusCode: 200, + headers: new Headers(), + contentType: 'application/json', + } as unknown as ResponseContext; + } + + return { successCriteriaCheck: true, expectCheck: true }; + } + ); + + (checkCriteria as jest.Mock).mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + severity: 'error', + }, + ]); + + const context = { + ...basicCTX, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + }, + { + stepId: 'failure-action-step', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + }, + } as unknown as TestContext; + + await runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(displayChecks).toHaveBeenCalled(); + // expect(checkCriteria).toHaveBeenCalledTimes(3); + }); + + it('should execute onFailure step criteria with failed retry', async () => { + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + type: 'retry', + retryAfter: 1000, + retryLimit: 2, + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + // @ts-ignore + (callAPIAndAnalyzeResults as jest.Mock).mockImplementation(async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: false, + message: '', + severity: 'error', + }, + ]; + + if (step.stepId === 'get-bird') { + step.response = { + body: { + bird: '🐦', + name: 'hawk', + }, + statusCode: 200, + headers: new Headers(), + contentType: 'application/json', + } as unknown as ResponseContext; + } + + return { successCriteriaCheck: false, expectCheck: true }; + }); + + (checkCriteria as jest.Mock).mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: false, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + severity: 'error', + }, + ]); + + const context = { + ...basicCTX, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onFailure: [ + { + name: 'success-action', + stepId: 'failure-action-step', + type: 'goto', + criteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + checks: [], + }, + { + stepId: 'failure-action-step', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + }, + } as unknown as TestContext; + + await runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(displayChecks).toHaveBeenCalled(); + expect(checkCriteria).toHaveBeenCalledTimes(2); + }); + + it('should throw an error when the step in context does not specify a workflowId and the `in` property missing', async () => { + const stepOne = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + parameters: [ + { + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + checks: [], + } as unknown as Step; + const workflowName = 'get-bird-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + + const context = { + ...basicCTX, + ...{ + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + parameters: [ + { + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + successCriteria: [{ condition: '$statusCode == 200' }], + checks: [], + }, + ], + }, + ], + }, + } as unknown as TestContext; + + await expect(() => + runStep({ + step: stepOne, + ctx: context, + workflowName, + parentStepId, + parentWorkflowId, + }) + ).rejects.toThrow(`Parameter "in" is required for ${stepOne.stepId} step`); + }); + + it('should run step with workflowId and set correct outputs', async () => { + const step = { + stepId: 'get-bird', + workflowId: 'reusable-workflow', + outputs: { + stepOutput: '$outputs.reusableWorkflowOutput', + }, + checks: [], + } as unknown as Step; + const workflowName = 'test-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + const localCTX = { + $request: undefined, + $response: undefined, + $env: {}, + $faker: { + address: {}, + date: {}, + number: {}, + string: {}, + }, + $sourceDescriptions: { + 'reusable-api': { + arazzo: '1.0.1', + info: { + title: 'Reusable API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + workflows: [ + { + workflowId: 'reusable-get-bird-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + steps: [ + { + stepId: 'reusable-first-step-one', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + { + stepId: 'reusable-delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + ], + }, + { + workflowId: 'reusable-get-bird-workflow-2', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + steps: [ + { + stepId: 'reusable-first-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + { + stepId: 'reusable-delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + ], + outputs: { + wowStatusCode: '$steps.reusable-delete-step.response.statusCode', + }, + }, + { + workflowId: 'second-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + dependsOn: ['reusable-get-bird-workflow', 'reusable-get-bird-workflow-2'], + steps: [ + { + stepId: 'delete-small-mock', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + onSuccess: [ + { + name: 'testRetry', + type: 'goto', + workflowId: 'reusable-get-bird-workflow', + }, + ], + }, + ], + }, + ], + }, + cats: { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/fact': { + get: { + tags: ['Facts'], + summary: 'Get Random Fact', + description: 'Returns a random fact', + operationId: 'getRandomFact', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + '404': { + description: 'Fact not found', + }, + }, + }, + }, + '/facts': { + get: { + tags: ['Facts'], + summary: 'Get a list of facts', + description: 'Returns a a list of facts', + operationId: 'getFacts', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [ + { + url: 'https://catfact.ninja/', + }, + ], + info: { + title: 'Cat Facts API', + version: '1.0', + }, + }, + }, + sourceDescriptions: [ + { name: 'cats', type: 'openapi', url: 'api-samples/cats.yaml' }, + { name: 'reusable-api', type: 'arazzo', url: 'small.yml' }, + ], + workflows: [ + { + workflowId: 'test-workflow', + steps: [ + { + stepId: 'get-bird', + workflowId: 'reusable-workflow', + outputs: { + stepOutput: '$outputs.reusableWorkflowOutput', + }, + checks: [], + }, + ], + }, + { + workflowId: 'reusable-workflow', + steps: [ + { + stepId: 'reusable-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + checks: [], + }, + ], + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }, + ], + $workflows: { + 'test-workflow': { + steps: { + 'get-bird': {}, + }, + }, + 'reusable-workflow': { + steps: { + 'reusable-step': {}, + }, + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }, + }, + $steps: {}, + harLogs: {}, + options: { + workflowPath: 'runStepTest.yml', + workflow: undefined, + skip: undefined, + verbose: undefined, + harLogsFile: 'har-output', + metadata: { + _: [], + files: ['runStepTest.yml'], + $0: 'respect', + file: 'runStepTest.yml', + }, + input: undefined, + }, + info: { title: 'Test API', version: '1.0' }, + arazzo: '1.0.1', + $outputs: {}, + } as unknown as TestContext; + + // @ts-ignore + runWorkflow.mockImplementationOnce(() => { + return { + workflowId: 'reusable-workflow', + steps: [ + { + stepId: 'reusable-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + checks: [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + message: '', + }, + ], + response: { + body: {}, + statusCode: 204, + headers: {}, + contentType: 'text/plain', + }, + }, + ], + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }; + }); + + await runStep({ + step, + ctx: localCTX, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(runWorkflow).toHaveBeenCalled(); + expect(localCTX.$steps['get-bird'].outputs).toEqual({ stepOutput: 'Hello, world!' }); + }); + + it('should run step with workflowId from external workflowSpec', async () => { + const step = { + stepId: 'get-bird', + workflowId: '$sourceDescriptions.reusable-api.workflows.reusable-external-workflow', + checks: [], + } as unknown as Step; + const parentWorkflowId = undefined; + const parentStepId = undefined; + const localCTX = { + $request: undefined, + $response: undefined, + $env: {}, + $faker: { + address: {}, + date: {}, + number: {}, + string: {}, + }, + $sourceDescriptions: { + 'reusable-api': { + arazzo: '1.0.1', + info: { + title: 'Reusable API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + { name: 'reusable-api', type: 'arazzo', url: 'small.yml' }, + ], + workflows: [ + { + workflowId: 'reusable-external-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + steps: [ + { + stepId: 'reusable-first-step-one', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + { + stepId: 'reusable-delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + ], + }, + { + workflowId: 'reusable-get-bird-workflow-2', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + steps: [ + { + stepId: 'reusable-first-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + { + stepId: 'reusable-delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + ], + outputs: { + wowStatusCode: '$steps.reusable-delete-step.response.statusCode', + }, + }, + { + workflowId: 'second-workflow', + dependsOn: ['reusable-get-bird-workflow', 'reusable-get-bird-workflow-2'], + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + steps: [ + { + stepId: 'delete-small-mock', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + onSuccess: [ + { + name: 'testRetry', + type: 'goto', + workflowId: 'reusable-get-bird-workflow', + }, + ], + }, + ], + }, + ], + }, + cats: { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/fact': { + get: { + tags: ['Facts'], + summary: 'Get Random Fact', + description: 'Returns a random fact', + operationId: 'getRandomFact', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + '404': { + description: 'Fact not found', + }, + }, + }, + }, + '/facts': { + get: { + tags: ['Facts'], + summary: 'Get a list of facts', + description: 'Returns a a list of facts', + operationId: 'getFacts', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [ + { + url: 'https://catfact.ninja/', + }, + ], + info: { + title: 'Cat Facts API', + version: '1.0', + }, + }, + }, + sourceDescriptions: [ + { name: 'cats', type: 'openapi', url: 'api-samples/cats.yaml' }, + { name: 'reusable-api', type: 'arazzo', url: 'small.yml' }, + ], + workflows: [ + { + workflowId: 'test-workflow', + steps: [ + { + stepId: 'get-bird', + workflowId: '$sourceDescriptions.reusable-api.workflows.reusable-external-workflow', + checks: [], + }, + ], + }, + { + workflowId: 'reusable-external-workflow', + steps: [ + { + stepId: 'reusable-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + checks: [], + }, + ], + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }, + ], + $workflows: { + 'test-workflow': { + steps: { + 'get-bird': {}, + }, + }, + 'reusable-external-workflow': { + steps: { + 'reusable-step': {}, + }, + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }, + }, + $steps: {}, + harLogs: {}, + options: { + workflowPath: 'runStepTest.yml', + workflow: undefined, + skip: undefined, + verbose: undefined, + harLogsFile: 'har-output', + metadata: { + _: [], + files: ['runStepTest.yml'], + $0: 'respect', + file: 'runStepTest.yml', + }, + input: undefined, + }, + info: { title: 'Test API', version: '1.0' }, + arazzo: '1.0.1', + $outputs: {}, + } as unknown as TestContext; + + // @ts-ignore + runWorkflow.mockImplementationOnce(() => { + return { + workflowId: 'reusable-external-workflow', + steps: [ + { + stepId: 'reusable-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + checks: [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + message: '', + }, + ], + response: { + body: {}, + statusCode: 204, + headers: {}, + contentType: 'text/plain', + }, + }, + ], + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }; + }); + + await runStep({ + step, + ctx: localCTX, + workflowName: 'test-workflow', + parentStepId, + parentWorkflowId, + }); + + // @ts-ignore + expect(runWorkflow).toHaveBeenCalledTimes(1); + }); + + it('should run step with workflowId from external workflow and populate inputs from the parameters', async () => { + const step = { + stepId: 'get-bird', + workflowId: '$sourceDescriptions.reusable-api.workflows.reusable-external-workflow', + parameters: [ + { + name: 'workflowLevelParam', + value: 'workflowParameterValue', + }, + ], + checks: [], + } as unknown as Step; + const parentWorkflowId = undefined; + const parentStepId = undefined; + const localCTX = { + $request: undefined, + $response: undefined, + $env: {}, + $faker: { + address: {}, + date: {}, + number: {}, + string: {}, + }, + $sourceDescriptions: { + 'reusable-api': { + arazzo: '1.0.1', + info: { + title: 'Reusable API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + { name: 'reusable-api', type: 'arazzo', url: 'small.yml' }, + ], + workflows: [ + { + workflowId: 'reusable-external-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + inputs: { + type: 'object', + properties: { + workflowLevelParam: { + type: 'string', + description: 'Some input parameter', + }, + }, + }, + steps: [ + { + stepId: 'reusable-first-step-one', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + { + stepId: 'reusable-delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + ], + }, + { + workflowId: 'reusable-get-bird-workflow-2', + parameters: [ + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + steps: [ + { + stepId: 'reusable-first-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + { + stepId: 'reusable-delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + ], + outputs: { + wowStatusCode: '$steps.reusable-delete-step.response.statusCode', + }, + }, + { + workflowId: 'second-workflow', + dependsOn: ['reusable-get-bird-workflow', 'reusable-get-bird-workflow-2'], + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + steps: [ + { + stepId: 'delete-small-mock', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + onSuccess: [ + { + name: 'testRetry', + type: 'goto', + workflowId: 'reusable-get-bird-workflow', + }, + ], + }, + ], + }, + ], + }, + cats: { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/fact': { + get: { + tags: ['Facts'], + summary: 'Get Random Fact', + description: 'Returns a random fact', + operationId: 'getRandomFact', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + '404': { + description: 'Fact not found', + }, + }, + }, + }, + '/facts': { + get: { + tags: ['Facts'], + summary: 'Get a list of facts', + description: 'Returns a a list of facts', + operationId: 'getFacts', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [ + { + url: 'https://catfact.ninja/', + }, + ], + info: { + title: 'Cat Facts API', + version: '1.0', + }, + }, + }, + sourceDescriptions: [ + { name: 'cats', type: 'openapi', url: 'api-samples/cats.yaml' }, + { name: 'reusable-api', type: 'arazzo', url: 'small.yml' }, + ], + workflows: [ + { + workflowId: 'test-workflow', + steps: [ + { + stepId: 'get-bird', + workflowId: '$sourceDescriptions.reusable-api.workflows.reusable-external-workflow', + parameters: [ + { + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + checks: [], + }, + ], + }, + { + workflowId: 'reusable-external-workflow', + steps: [ + { + stepId: 'reusable-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + checks: [], + }, + ], + inputs: { + type: 'object', + properties: { + workflowLevelParam: { + type: 'string', + description: 'Some input', + }, + }, + }, + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }, + ], + $workflows: { + 'test-workflow': { + steps: { + 'get-bird': {}, + }, + inputs: {}, + }, + 'reusable-external-workflow': { + steps: { + 'reusable-step': {}, + }, + inputs: { + workflowLevelParam: 'workflowParameterValue', + }, + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }, + }, + $steps: {}, + harLogs: {}, + options: { + workflowPath: 'runStepTest.yml', + workflow: undefined, + skip: undefined, + verbose: undefined, + harLogsFile: 'har-output', + metadata: { + _: [], + files: ['runStepTest.yml'], + $0: 'respect', + file: 'runStepTest.yml', + }, + input: undefined, + }, + info: { title: 'Test API', version: '1.0' }, + arazzo: '1.0.1', + $outputs: {}, + $inputs: {}, + } as unknown as TestContext; + + (resolveWorkflowContext as jest.Mock).mockImplementation(() => { + return { ...localCTX }; + }); + + // @ts-ignore + runWorkflow.mockImplementationOnce(() => { + return { + workflowId: 'reusable-external-workflow', + steps: [ + { + stepId: 'reusable-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + checks: [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + message: '', + }, + ], + response: { + body: {}, + statusCode: 204, + headers: {}, + contentType: 'text/plain', + }, + }, + ], + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }; + }); + + await runStep({ + step, + ctx: localCTX, + workflowName: 'test-workflow', + parentStepId, + parentWorkflowId, + }); + + expect(resolveWorkflowContext).toHaveBeenCalledWith( + '$sourceDescriptions.reusable-api.workflows.reusable-external-workflow', + { + inputs: { + properties: { + workflowLevelParam: { + description: 'Some input parameter', + type: 'string', + }, + }, + type: 'object', + }, + steps: [ + { + stepId: 'reusable-first-step-one', + successCriteria: [{ condition: '$statusCode == 204' }], + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + }, + { + stepId: 'reusable-delete-step', + successCriteria: [{ condition: '$statusCode == 204' }], + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + }, + ], + workflowId: 'reusable-external-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + }, + localCTX + ); + expect(runWorkflow).toHaveBeenCalledTimes(1); + }); + + it('should run step with not existing workflowId and populate step.checks with an error', async () => { + const step = { + stepId: 'get-bird', + workflowId: '$sourceDescriptions.wrong-reusable-api.workflows.reusable-external-workflow', + outputs: { + stepOutput: '$outputs.reusableWorkflowOutput', + }, + checks: [], + } as unknown as Step; + const workflowName = 'test-workflow'; + const parentWorkflowId = undefined; + const parentStepId = undefined; + const localCTX = { + apiClient, + $request: undefined, + $response: undefined, + $env: {}, + $faker: { + address: {}, + date: {}, + number: {}, + string: {}, + }, + $sourceDescriptions: { + 'reusable-api': { + arazzo: '1.0.1', + info: { + title: 'Reusable API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + workflows: [ + { + workflowId: 'reusable-get-bird-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + steps: [ + { + stepId: 'reusable-first-step-one', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + { + stepId: 'reusable-delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + ], + }, + { + workflowId: 'reusable-get-bird-workflow-2', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + steps: [ + { + stepId: 'reusable-first-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + { + stepId: 'reusable-delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + }, + ], + outputs: { + wowStatusCode: '$steps.reusable-delete-step.response.statusCode', + }, + }, + { + workflowId: 'second-workflow', + dependsOn: ['reusable-get-bird-workflow', 'reusable-get-bird-workflow-2'], + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + steps: [ + { + stepId: 'delete-small-mock', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + onSuccess: [ + { + name: 'testRetry', + type: 'goto', + workflowId: 'reusable-get-bird-workflow', + }, + ], + }, + ], + }, + ], + }, + cats: { + paths: { + '/breeds': { + get: { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + '/fact': { + get: { + tags: ['Facts'], + summary: 'Get Random Fact', + description: 'Returns a random fact', + operationId: 'getRandomFact', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + '404': { + description: 'Fact not found', + }, + }, + }, + }, + '/facts': { + get: { + tags: ['Facts'], + summary: 'Get a list of facts', + description: 'Returns a a list of facts', + operationId: 'getFacts', + parameters: [ + { + name: 'max_length', + in: 'query', + description: 'maximum length of returned fact', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'CatFact model', + description: 'CatFact', + properties: { + fact: { + title: 'Fact', + description: 'Fact', + type: 'string', + format: 'string', + }, + length: { + title: 'Length', + description: 'Length', + type: 'integer', + format: 'int32', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [ + { + url: 'https://catfact.ninja/', + }, + ], + info: { + title: 'Cat Facts API', + version: '1.0', + }, + }, + }, + severity: DEFAULT_SEVERITY_CONFIGURATION, + sourceDescriptions: [ + { name: 'cats', type: 'openapi', url: 'api-samples/cats.yaml' }, + { name: 'reusable-api', type: 'arazzo', url: 'small.yml' }, + ], + workflows: [ + { + workflowId: 'test-workflow', + steps: [ + { + stepId: 'get-bird', + workflowId: 'reusable-workflow', + outputs: { + stepOutput: '$outputs.reusableWorkflowOutput', + }, + checks: [], + }, + ], + }, + { + workflowId: 'reusable-workflow', + steps: [ + { + stepId: 'reusable-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + checks: [], + }, + ], + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }, + ], + $workflows: { + 'test-workflow': { + steps: { + 'get-bird': {}, + }, + }, + 'reusable-workflow': { + steps: { + 'reusable-step': {}, + }, + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }, + }, + $steps: {}, + harLogs: {}, + options: { + workflowPath: 'runStepTest.yml', + workflow: undefined, + skip: undefined, + verbose: undefined, + harLogsFile: 'har-output', + metadata: { + _: [], + files: ['runStepTest.yml'], + $0: 'respect', + file: 'runStepTest.yml', + }, + input: undefined, + }, + info: { title: 'Test API', version: '1.0' }, + arazzo: '1.0.1', + $outputs: {}, + } as unknown as TestContext; + + // @ts-ignore + runWorkflow.mockImplementationOnce(() => { + return () => { + return { + workflowId: 'reusable-workflow', + steps: [ + { + stepId: 'reusable-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [{ condition: '$statusCode == 204' }], + checks: [ + { + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + message: '', + }, + ], + response: { + body: {}, + statusCode: 204, + headers: {}, + contentType: 'text/plain', + }, + }, + ], + outputs: { + reusableWorkflowOutput: 'Hello, world!', + }, + }; + }; + }); + + await runStep({ + step, + ctx: localCTX, + workflowName, + parentStepId, + parentWorkflowId, + }); + + expect(runWorkflow).not.toHaveBeenCalled(); + expect(cleanColors(step?.checks[0]?.message || '')).toEqual( + 'Workflow $sourceDescriptions.wrong-reusable-api.workflows.reusable-external-workflow not found.' + ); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/runner/__snapshots__/create-workflow-runner.test.ts.snap b/packages/respect-core/src/modules/__tests__/flow-runner/runner/__snapshots__/create-workflow-runner.test.ts.snap new file mode 100644 index 0000000000..93ec01169d --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/runner/__snapshots__/create-workflow-runner.test.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`runWorkflow should run workflow within step execution 1`] = ` +[MockFunction] { + "calls": [ + [ + " Running step parentStepId workflow parentWorkflowId", + ], + ], + "results": [ + { + "type": "return", + "value": undefined, + }, + ], +} +`; diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/runner/__snapshots__/run-test-file.test.ts.snap b/packages/respect-core/src/modules/__tests__/flow-runner/runner/__snapshots__/run-test-file.test.ts.snap new file mode 100644 index 0000000000..6b333e81ff --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/runner/__snapshots__/run-test-file.test.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`runTestFile should throw Invalid file configuration error when contains lint errors 1`] = `[Error: Invalid file configuration test.yaml]`; diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/runner/create-workflow-runner.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/runner/create-workflow-runner.test.ts new file mode 100644 index 0000000000..65b4fe24f6 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/runner/create-workflow-runner.test.ts @@ -0,0 +1,479 @@ +import type { ApiFetcher } from '../../../../utils/api-fetcher'; +import type { Workflow, TestContext } from '../../../../types'; + +import { runWorkflow, DEFAULT_SEVERITY_CONFIGURATION } from '../../../flow-runner'; +import { DefaultLogger } from '../../../../utils/logger/logger'; + +const logger = DefaultLogger.getInstance(); +describe('runWorkflow', () => { + const fileName = 'test.yaml'; + it('should run workflow', async () => { + const apiClient = { + setDescriptionParameters: jest.fn(), + getRequestHeaderParams: jest.fn(), + getRequestParams: jest.fn(), + setDefaultsParameters: jest.fn(), + setDisconnectDescription: jest.fn(), + setPath: jest.fn(), + setDescriptionRequestBody: jest.fn(), + setDescriptionResponses: jest.fn(), + setDescriptionContentType: jest.fn(), + setParameters: jest.fn(), + setTestCaseRequestBody: jest.fn(), + setMethod: jest.fn(), + setApiBase: jest.fn(), + fetchResult: jest.fn(), + getVerboseResponseLogs: jest.fn(), + } as unknown as ApiFetcher; + + (apiClient.fetchResult as jest.Mock).mockResolvedValue({ + statusCode: 200, + }); + + const workflow = { + workflowId: 'test', + steps: [ + { + stepId: 'test', + 'x-operation': { + url: 'http://localhost:3000/test', + method: 'GET', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + } as unknown as Workflow; + + const ctx = { + apiClient, + workflows: [workflow], + $workflows: { + test: { + inputs: {}, + steps: { + test: { + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + response: { + statusCode: 200, + }, + }, + }, + }, + }, + $steps: { + test: { + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + response: { + statusCode: 200, + }, + }, + }, + severity: DEFAULT_SEVERITY_CONFIGURATION, + sourceDescriptions: [ + { + name: 'test', + type: 'openapi', + url: 'openapi.yaml', + }, + ], + options: { + verbose: false, + workflowPath: fileName, + }, + } as unknown as TestContext; + + await runWorkflow({ workflowInput: 'test', ctx }); + + expect(apiClient.fetchResult).toBeCalled(); + }); + + it('should return if no steps', async () => { + const apiClient = { + fetchResult: jest.fn(), + } as unknown as ApiFetcher; + + const workflow = { + workflowId: 'test', + steps: [], + } as unknown as Workflow; + + const ctx = { + apiClient, + workflows: [workflow], + options: { + verbose: false, + workflowPath: fileName, + }, + } as unknown as TestContext; + + await runWorkflow({ workflowInput: 'test', ctx }); + + expect(apiClient.fetchResult).not.toBeCalled(); + }); + + it('should throw an error if no workflow exists', async () => { + const apiClient = { + fetchResult: jest.fn(), + } as unknown as ApiFetcher; + + const ctx = { + apiClient, + workflows: [], + options: { + verbose: false, + workflowPath: fileName, + }, + } as unknown as TestContext; + await expect(runWorkflow({ workflowInput: 'test', ctx })).rejects.toThrowError(); + expect(apiClient.fetchResult).not.toBeCalled(); + }); + + it('should set workflow outputs', async () => { + const apiClient = { + fetchResult: jest.fn(), + getVerboseResponseLogs: jest.fn(), + } as unknown as ApiFetcher; + + (apiClient.fetchResult as jest.Mock).mockResolvedValue({ + statusCode: 200, + }); + + const workflow = { + workflowId: 'test', + outputs: [ + { + test: 'test', + }, + ], + steps: [ + { + stepId: 'test', + 'x-operation': { + url: 'http://localhost:3000/test', + method: 'GET', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + } as unknown as Workflow; + + const ctx = { + apiClient, + workflows: [workflow], + $workflows: { + test: { + outputs: [ + { + test: 'test', + }, + ], + inputs: {}, + steps: { + test: { + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + response: { + statusCode: 200, + }, + }, + }, + }, + }, + $steps: { + test: { + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + response: { + statusCode: 200, + }, + }, + }, + sourceDescriptions: [ + { + name: 'test', + type: 'openapi', + url: 'openapi.yaml', + }, + ], + severity: DEFAULT_SEVERITY_CONFIGURATION, + options: { + verbose: false, + workflowPath: fileName, + }, + $outputs: {}, + } as unknown as TestContext; + + await runWorkflow({ workflowInput: 'test', ctx }); + + expect(ctx.$outputs?.test).toEqual({ outputs: [{ test: 'test' }] }); + expect(ctx.$workflows.test.outputs).toEqual([{ test: 'test' }]); + }); + + it('should return if workflow does not have steps', async () => { + const apiClient = { + fetchResult: jest.fn(), + } as unknown as ApiFetcher; + + (apiClient.fetchResult as jest.Mock).mockResolvedValue({ + statusCode: 200, + }); + + const workflow = { + workflowId: 'test', + outputs: [ + { + test: 'test', + }, + ], + steps: [], + } as unknown as Workflow; + + const ctx = { + apiClient, + workflows: [workflow], + $workflows: { + test: { + outputs: [ + { + test: 'test', + }, + ], + inputs: {}, + steps: { + test: { + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + response: { + statusCode: 200, + }, + }, + }, + }, + }, + $steps: { + test: { + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + response: { + statusCode: 200, + }, + }, + }, + sourceDescriptions: [ + { + name: 'test', + type: 'openapi', + url: 'openapi.yaml', + }, + ], + options: { + verbose: false, + workflowPath: fileName, + }, + $outputs: {}, + } as unknown as TestContext; + + await runWorkflow({ workflowInput: 'test', ctx }); + + expect(apiClient.fetchResult).not.toBeCalled(); + }); + + it('should run workflow within step execution', async () => { + const mockLogger = jest.spyOn(logger, 'log').mockImplementation(); + const apiClient = { + fetchResult: jest.fn(), + } as unknown as ApiFetcher; + + (apiClient.fetchResult as jest.Mock).mockResolvedValue({ + statusCode: 200, + }); + + const workflow = { + workflowId: 'test', + outputs: [ + { + test: 'test', + }, + ], + steps: [], + } as unknown as Workflow; + + const ctx = { + apiClient, + workflows: [workflow], + $workflows: { + test: { + outputs: [ + { + test: 'test', + }, + ], + inputs: {}, + steps: {}, + }, + parentWorkflowId: { + outputs: [ + { + test: 'test', + }, + ], + inputs: {}, + steps: {}, + }, + }, + $steps: {}, + sourceDescriptions: [ + { + name: 'test', + type: 'openapi', + url: 'openapi.yaml', + }, + ], + options: { + verbose: false, + workflowPath: fileName, + }, + $outputs: {}, + } as unknown as TestContext; + + await runWorkflow({ + workflowInput: 'test', + parentStepId: 'parentStepId', + parentWorkflowId: 'parentWorkflowId', + ctx, + }); + + expect(mockLogger).toMatchSnapshot(); + + mockLogger.mockRestore(); + }); + + it('should accept workflow as an input', async () => { + const apiClient = { + fetchResult: jest.fn(), + getVerboseResponseLogs: jest.fn(), + } as unknown as ApiFetcher; + + (apiClient.fetchResult as jest.Mock).mockResolvedValue({ + statusCode: 200, + }); + + const workflow = { + workflowId: 'test', + outputs: [ + { + test: 'test', + }, + ], + steps: [ + { + stepId: 'test', + 'x-operation': { + url: 'http://localhost:3000/test', + method: 'GET', + }, + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + }, + ], + } as unknown as Workflow; + + const ctx = { + apiClient, + workflows: [workflow], + $workflows: { + test: { + outputs: [ + { + test: 'test', + }, + ], + inputs: {}, + steps: { + test: { + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + response: { + statusCode: 200, + }, + }, + }, + }, + parentWorkflowId: { + inputs: {}, + outputs: [], + steps: {}, + }, + }, + $steps: { + test: { + successCriteria: [ + { + condition: '$statusCode == 200', + }, + ], + response: { + statusCode: 200, + }, + }, + }, + sourceDescriptions: [ + { + name: 'test', + type: 'openapi', + url: 'openapi.yaml', + }, + ], + severity: DEFAULT_SEVERITY_CONFIGURATION, + options: { + verbose: false, + workflowPath: fileName, + }, + $outputs: {}, + } as unknown as TestContext; + + await runWorkflow({ + workflowInput: workflow, + parentStepId: 'parentStepId', + parentWorkflowId: 'parentWorkflowId', + ctx, + }); + + expect(ctx.$outputs?.test).toEqual({ outputs: [{ test: 'test' }] }); + expect(ctx.$workflows.test.outputs).toEqual([{ test: 'test' }]); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/runner/resolve-workflow-context.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/runner/resolve-workflow-context.test.ts new file mode 100644 index 0000000000..df4e55e775 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/runner/resolve-workflow-context.test.ts @@ -0,0 +1,534 @@ +import { resolveWorkflowContext, createTestContext } from '../../../flow-runner'; +import { ApiFetcher } from '../../../../utils/api-fetcher'; + +jest.mock('../../../flow-runner/context/create-test-context'); + +describe('resolveWorkflowContext', () => { + const workflowId = '$sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets'; + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const resolvedWorkflow = { + workflowId: 'get-museum-tickets', + description: 'This workflow demonstrates how to buy tickets for the museum.', + parameters: [ + { + in: 'header', + name: 'Authorization', + value: 'Basic Og==', + }, + ], + steps: [ + { + stepId: 'buy-tickets', + description: + 'Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description.', + operationId: 'buyMuseumTickets', + requestBody: { + payload: { + ticketType: 'general', + ticketDate: '2023-09-07', + email: 'todd@example.com', + }, + }, + successCriteria: [ + { + condition: '$statusCode == 201', + }, + ], + outputs: { + ticketId: '$response.body.ticketId', + }, + }, + ], + outputs: { + ticketId: '$steps.buy-tickets.outputs.ticketId', + }, + } as any; + const commonCtx = { + sourceDescriptions: [ + { name: 'museum-api', type: 'openapi', url: 'museum-api.yaml' }, + { + name: 'tickets-from-museum-api', + type: 'arazzo', + url: 'museum-tickets.yaml', + }, + ], + $sourceDescriptions: { + 'local-api': {}, + 'museum-api': { + paths: { + '/museum-hours': { + get: { + summary: 'Get museum hours', + description: 'Get upcoming museum operating hours.', + operationId: 'getMuseumHours', + tags: ['Operations'], + parameters: [ + { + name: 'startDate', + in: 'query', + description: + "Starting date to retrieve future operating hours from. Defaults to today's date.", + schema: { + type: 'string', + format: 'date', + example: '2023-02-23', + }, + }, + { + name: 'page', + in: 'query', + description: 'Page number to retrieve.', + schema: { + type: 'integer', + default: 1, + example: 2, + }, + }, + { + name: 'limit', + in: 'query', + description: 'Number of days per page.', + schema: { + type: 'integer', + default: 10, + maximum: 30, + example: 15, + }, + }, + ], + responses: { + '200': { + description: 'Success.', + content: { + 'application/json': { + schema: { + description: 'List of museum operating hours for consecutive days.', + type: 'array', + items: { + description: 'Daily operating hours for the museum.', + type: 'object', + properties: { + date: { + description: 'Date the operating hours apply to.', + example: '2024-12-31', + type: 'string', + format: 'date', + }, + timeOpen: { + type: 'string', + pattern: '^([01]\\d|2[0-3]):?([0-5]\\d)$', + description: + 'Time the museum opens on a specific date. Uses 24 hour time format (`HH:mm`).', + example: '09:00', + }, + timeClose: { + description: + 'Time the museum closes on a specific date. Uses 24 hour time format (`HH:mm`).', + type: 'string', + pattern: '^([01]\\d|2[0-3]):?([0-5]\\d)$', + example: '18:00', + }, + }, + required: ['date', 'timeOpen', 'timeClose'], + }, + }, + examples: { + default_example: { + summary: 'Get hours response', + value: [ + { + date: '2023-09-11', + timeOpen: '09:00', + timeClose: '18:00', + }, + { + date: '2023-09-12', + timeOpen: '09:00', + timeClose: '18:00', + }, + { + date: '2023-09-13', + timeOpen: '09:00', + timeClose: '18:00', + }, + { + date: '2023-09-14', + timeOpen: '09:00', + timeClose: '18:00', + }, + { + date: '2023-09-15', + timeOpen: '10:00', + timeClose: '16:00', + }, + { + date: '2023-09-18', + timeOpen: '09:00', + timeClose: '18:00', + }, + { + date: '2023-09-19', + timeOpen: '09:00', + timeClose: '18:00', + }, + { + date: '2023-09-20', + timeOpen: '09:00', + timeClose: '18:00', + }, + { + date: '2023-09-21', + timeOpen: '09:00', + timeClose: '18:00', + }, + { + date: '2023-09-22', + timeOpen: '10:00', + timeClose: '16:00', + }, + ], + }, + }, + }, + }, + }, + '400': { + description: 'Bad request.', + content: { + 'application/problem+json': { + schema: { + type: 'object', + properties: { + type: { + type: 'string', + example: 'object', + }, + title: { + type: 'string', + example: 'Validation failed', + }, + }, + }, + }, + }, + }, + '404': { + description: 'Not found.', + content: { + 'application/problem+json': { + schema: { + type: 'object', + properties: { + type: { + type: 'string', + example: 'object', + }, + title: { + type: 'string', + example: 'Validation failed', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [ + { + url: 'https://redocly.com/_mock/docs/openapi/museum-api/', + }, + ], + info: { + title: 'Redocly Museum API', + description: + 'Imaginary, but delightful Museum API for interacting with museum services and information. Built with love by Redocly.', + version: '1.1.1', + termsOfService: 'https://redocly.com/subscription-agreement/', + contact: { + email: 'team@redocly.com', + url: 'https://redocly.com/docs/cli/', + }, + license: { + name: 'MIT', + url: 'https://opensource.org/license/mit/', + }, + }, + }, + 'tickets-from-museum-api': { + arazzo: '1.0.1', + info: { + title: 'Redocly Museum API Tickets', + description: + 'A part of imaginary, but delightful Museum API for interacting with museum services and information. Built with love by Redocly.', + version: '1.0.0', + }, + sourceDescriptions: [ + { + name: 'museum-api', + type: 'openapi', + url: 'museum-api.yaml', + }, + ], + workflows: [ + { + workflowId: 'get-museum-tickets', + description: 'This workflow demonstrates how to buy tickets for the museum.', + parameters: [ + { + in: 'header', + name: 'Authorization', + value: 'Basic Og==', + }, + ], + steps: [ + { + stepId: 'buy-tickets', + description: + 'Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description.', + operationId: 'buyMuseumTickets', + requestBody: { + payload: { + ticketType: 'general', + ticketDate: '2023-09-07', + email: 'todd@example.com', + }, + }, + successCriteria: [ + { + condition: '$statusCode == 201', + }, + ], + outputs: { + ticketId: '$response.body.ticketId', + }, + }, + ], + outputs: { + ticketId: '$steps.buy-tickets.outputs.ticketId', + }, + }, + ], + components: {}, + }, + }, + options: { + workflowPath: 'examples/museum-api/museum-api-test.yaml', + workflow: undefined, + skip: undefined, + verbose: undefined, + harLogsFile: 'har-output', + metadata: { + _: ['run'], + files: ['examples/museum-api/museum-api-test.yaml'], + $0: 'respect', + file: 'examples/museum-api/museum-api-test.yaml', + }, + input: undefined, + }, + apiClient, + } as any; + + it('should call createTestContext with the correct parameters when sourceDescriptionId is undefined', async () => { + const apiClient = new ApiFetcher({ + harLogs: undefined, + }); + const workflowId = undefined; + const resolvedWorkflow = {} as any; + const ctx = { + $sourceDescriptions: {}, + options: {}, + testDescription: {}, + apiClient, + } as any; + + await resolveWorkflowContext(workflowId, resolvedWorkflow, ctx); + + expect(createTestContext).toHaveBeenCalledWith({}, {}, apiClient); + }); + + it('should call createTestContext with the correct parameters when sourceDescriptionId is defined for arazzo type', async () => { + await resolveWorkflowContext(workflowId, resolvedWorkflow, commonCtx); + + expect(createTestContext).toHaveBeenCalledWith( + commonCtx.$sourceDescriptions['tickets-from-museum-api'], + { + input: undefined, + skip: undefined, + workflow: ['get-museum-tickets'], + workflowPath: expect.stringContaining('examples/museum-api/museum-tickets.yaml'), + }, + apiClient + ); + }); + + it('should call createTestContext with empty workflowPath when there are no ctx.sourceDescriptions', async () => { + const ctx = { + ...commonCtx, + ...{ sourceDescriptions: [] }, + } as any; + + await resolveWorkflowContext(workflowId, resolvedWorkflow, ctx); + + expect(createTestContext).toHaveBeenCalledWith( + ctx.$sourceDescriptions['tickets-from-museum-api'], + { + input: undefined, + skip: undefined, + workflow: ['get-museum-tickets'], + workflowPath: '', + }, + apiClient + ); + }); + + it('should call createTestContext with the correct parameters when sourceDescriptionId is defined for openapi type', async () => { + const workflowId = '$sourceDescriptions.museum-api'; + await resolveWorkflowContext(workflowId, resolvedWorkflow, commonCtx); + + expect(createTestContext).toHaveBeenCalledWith( + commonCtx.$sourceDescriptions['museum-api'], + { + input: undefined, + skip: undefined, + workflow: ['get-museum-tickets'], + workflowPath: 'museum-api.yaml', + }, + apiClient + ); + }); + + it('should throw an error when sourceDescription.type is not openapi or arazzo', async () => { + const localCtx = { + ...commonCtx, + sourceDescriptions: [ + { name: 'wrong-api', type: 'invalid', url: 'museum-api.yaml' }, + { + name: 'tickets-from-museum-api', + type: 'arazzo', + url: 'museum-tickets.yaml', + }, + ], + } as any; + const ctx = { + ...localCtx, + ...{ + $sourceDescriptions: { + 'wrong-api': { + paths: { + '/museum-hours': { + get: { + summary: 'Get museum hours', + description: 'Get upcoming museum operating hours.', + operationId: 'getMuseumHours', + tags: ['Operations'], + parameters: [ + { + name: 'startDate', + in: 'query', + description: + "Starting date to retrieve future operating hours from. Defaults to today's date.", + schema: { + type: 'string', + format: 'date', + example: '2023-02-23', + }, + }, + { + name: 'page', + in: 'query', + description: 'Page number to retrieve.', + schema: { + type: 'integer', + default: 1, + example: 2, + }, + }, + { + name: 'limit', + in: 'query', + description: 'Number of days per page.', + schema: { + type: 'integer', + default: 10, + maximum: 30, + example: 15, + }, + }, + ], + responses: { + '200': { + description: 'Success.', + content: { + 'application/json': { + schema: { + description: 'List of museum operating hours for consecutive days.', + type: 'array', + items: { + description: 'Daily operating hours for the museum.', + type: 'object', + properties: { + date: { + description: 'Date the operating hours apply to.', + example: '2024-12-31', + type: 'string', + format: 'date', + }, + }, + required: ['date', 'timeOpen', 'timeClose'], + }, + }, + examples: { + default_example: { + summary: 'Get hours response', + value: [ + { + date: '2023-09-11', + timeOpen: '09:00', + timeClose: '18:00', + }, + ], + }, + }, + }, + }, + }, + }, + }, + }, + }, + servers: [ + { + url: 'https://redocly.com/_mock/docs/openapi/museum-api/', + }, + ], + info: { + title: 'Redocly Museum API', + description: + 'Imaginary, but delightful Museum API for interacting with museum services and information. Built with love by Redocly.', + version: '1.1.1', + termsOfService: 'https://redocly.com/subscription-agreement/', + contact: { + email: 'team@redocly.com', + url: 'https://redocly.com/docs/cli/', + }, + license: { + name: 'MIT', + url: 'https://opensource.org/license/mit/', + }, + }, + }, + }, + }, + } as any; + const workflowId = '$sourceDescriptions.wrong-api.workflows.get-museum-tickets'; + + await expect(resolveWorkflowContext(workflowId, resolvedWorkflow, ctx)).rejects.toThrowError( + 'Unknown source description type invalid' + ); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/runner/run-test-file.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/runner/run-test-file.test.ts new file mode 100644 index 0000000000..2b0855dd74 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/runner/run-test-file.test.ts @@ -0,0 +1,605 @@ +import { makeDocumentFromString, lint, bundle } from '@redocly/openapi-core'; +import * as fs from 'node:fs'; + +import type { Step } from '../../../../types'; + +import { runTestFile, runStep } from '../../../flow-runner'; +import { readYaml } from '../../../../utils/yaml'; +import { writeFileSync } from 'node:fs'; + +jest.mock('../../../../utils/yaml', () => { + const originalModule = jest.requireActual('../../../../utils/yaml'); + return { + ...originalModule, // In case there are other exports you want to preserve + readYaml: jest.fn(), + }; +}); + +jest.mock('@redocly/openapi-core', () => ({ + ...jest.requireActual('@redocly/openapi-core'), // Preserve other exports + formatProblems: jest.fn(), // Mock formatProblems to do nothing + lint: jest.fn(), + bundle: jest.fn(), +})); + +jest.mock('../../../flow-runner/run-step', () => ({ + runStep: jest.fn(), +})); + +jest.mock('node:fs', () => { + const actual = jest.requireActual('node:fs'); + const mockExistsSync = jest.fn(); + const mockWriteFileSync = jest.fn(); + + return { + __esModule: true, + default: { + ...actual, + existsSync: mockExistsSync, + }, + existsSync: mockExistsSync, + }; +}); + +const mockExistsSync = fs.existsSync as jest.Mock; + +describe('runTestFile', () => { + beforeEach(() => { + mockExistsSync.mockImplementation((path: string) => { + // Always return true for test files to allow readYaml to be called + if (path.includes('test.yaml') || path.includes('test.yml')) { + return true; + } + + // Return true for source description files (both relative and absolute paths) + if (path.includes('cats.yaml')) { + return true; + } + // Return false for non-existing files + if (path.includes('not-existing')) { + return false; + } + return false; + }); + (readYaml as jest.Mock).mockReset().mockResolvedValue({ + openapi: '1.0.0', + info: { title: 'Cat Facts API', version: '1.0' }, + }); + (lint as jest.Mock).mockReset().mockResolvedValue([]); + (bundle as jest.Mock).mockReset(); + (runStep as jest.Mock).mockReset(); + }); + + afterAll(() => { + jest.resetAllMocks(); + }); + + it(`should trow error if filename is not correct`, async () => { + await expect(runTestFile({ file: '' }, {})).rejects.toThrowError('Invalid file name'); + }); + + it(`should trow error if file is not valid Arazzo test file`, async () => { + const mockDocument = makeDocumentFromString( + JSON.stringify({ + openapi: '1.0.0', + info: { title: 'Cat Facts API', version: '1.0' }, + }), + 'test.yml' + ); + + (readYaml as jest.Mock).mockResolvedValue(mockDocument.parsed); + + await expect(runTestFile({ file: 'test.yaml' }, {})).rejects.toThrowError( + 'No test files found. File test.yaml does not follows naming pattern "*.[yaml | yml | json]" or have not valid "Arazzo" description.' + ); + }); + + it('should throw Invalid file configuration error when contains lint errors', async () => { + const testDescription = { + workflows: [], + }; + + const mockDocument = makeDocumentFromString( + JSON.stringify({ + arazzo: '1.0.1', + info: { title: 'Cat Facts API', version: '1.0' }, + workflows: [ + { + workflowId: 'get-bird-workflow', + parameters: [{ in: 'header', name: 'IMF-KEY', value: 'test-key' }], + }, + ], + }), + 'test.yml' + ); + + (lint as jest.Mock).mockResolvedValueOnce([ + { + ruleId: 'spec', + severity: 'error', + message: 'The field `sourceDescriptions` must be present on this level.', + from: undefined, + location: [{}], + suggest: [], + }, + { + ruleId: 'spec', + severity: 'error', + message: 'Property `sourceDescriptionsd` is not expected here.', + suggest: ['sourceDescriptions'], + from: undefined, + location: [{}], + }, + ]); + + (readYaml as jest.Mock).mockResolvedValue(mockDocument.parsed); + + await expect( + runTestFile( + { + file: 'test.yaml', + testDescription, + }, + {} + ) + ).rejects.toMatchSnapshot(); + }); + + it('should call runStep once', async () => { + const mockDocument = makeDocumentFromString( + JSON.stringify({ + arazzo: '1.0.1', + info: { + title: 'Cat Facts API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + workflows: [ + { + workflowId: 'get-bird-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + steps: [ + { + stepId: 'delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + { + condition: '$response.header.content-type == "text/plain;charset=UTF-8"', + }, + ], + }, + ], + }, + ], + }), + 'api-test-framework/test.yml' + ); + + (readYaml as jest.Mock).mockResolvedValue(mockDocument.parsed); + (lint as jest.Mock).mockResolvedValueOnce([]); + (bundle as jest.Mock).mockResolvedValueOnce({ + bundle: { + parsed: mockDocument.parsed, + }, + }); + + await runTestFile( + { + file: 'test.yaml', + }, + {} + ); + + expect(runStep).toHaveBeenCalledTimes(1); + }); + + it('should call runStep multiple times including from dependsOn workflow', async () => { + const mockDocument = makeDocumentFromString( + JSON.stringify({ + arazzo: '1.0.1', + info: { + title: 'Cat Facts API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + workflows: [ + { + workflowId: 'get-bird-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + steps: [ + { + stepId: 'delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + ], + }, + { + workflowId: 'second-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + dependsOn: ['get-bird-workflow'], + steps: [ + { + stepId: 'delete-mock', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + ], + }, + ], + }), + 'test.yml' + ); + + (readYaml as jest.Mock).mockResolvedValue(mockDocument.parsed); + (lint as jest.Mock).mockResolvedValueOnce([]); + (bundle as jest.Mock).mockResolvedValueOnce({ + bundle: { + parsed: mockDocument.parsed, + }, + }); + + await runTestFile( + { + file: 'test.yaml', + }, + {} + ); + + // called 3 times, one for each step from each workflow and one from dependsOn + expect(runStep).toHaveBeenCalledTimes(3); + }, 8000); + + it('should throw an error when dependsOn has not existing workflowId', async () => { + const mockDocument = makeDocumentFromString( + JSON.stringify({ + arazzo: '1.0.1', + info: { + title: 'Cat Facts API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + workflows: [ + { + workflowId: 'get-bird-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + steps: [ + { + stepId: 'delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + ], + }, + { + workflowId: 'second-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + dependsOn: ['get-bird-workflow', 'not-existing-workflowId'], + steps: [ + { + stepId: 'delete-mock', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + ], + }, + ], + }), + 'api-test-framework/test.yml' + ); + + (readYaml as jest.Mock).mockResolvedValue(mockDocument.parsed); + (lint as jest.Mock).mockResolvedValueOnce([]); + (bundle as jest.Mock).mockResolvedValueOnce({ + bundle: { + parsed: mockDocument.parsed, + }, + }); + + await expect(runTestFile({ file: 'test.yaml' }, {})).rejects.toThrow( + expect.objectContaining({ + // @ts-ignore + message: expect.stringContaining('Workflow', 'not-existing-workflowId', 'not found'), + }) + ); + }, 8000); + + it('should throw an error when dependsOn has workflowId with not successful steps expectations', async () => { + const mockDocument = makeDocumentFromString( + JSON.stringify({ + arazzo: '1.0.1', + info: { + title: 'Cat Facts API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + ], + workflows: [ + { + workflowId: 'get-bird-workflow', + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + { + in: 'cookie', + name: 'workflowLevelParam', + value: 'workflowcookie', + }, + ], + steps: [ + { + stepId: 'delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + ], + }, + { + workflowId: 'second-workflow', + dependsOn: ['get-bird-workflow'], + parameters: [ + { + in: 'header', + name: 'IMF-KEY', + value: 'test-key', + }, + ], + steps: [ + { + stepId: 'delete-mock', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + ], + }, + ], + }), + 'api-test-framework/test.yml' + ); + + (readYaml as jest.Mock).mockResolvedValue(mockDocument.parsed); + (lint as jest.Mock).mockResolvedValueOnce([]); + (bundle as jest.Mock).mockResolvedValueOnce({ + bundle: { + parsed: mockDocument.parsed, + }, + }); + + (runStep as jest.Mock).mockImplementation(({ step }: { step: Step }) => { + step.checks = [{ name: step.stepId, pass: false, severity: 'error' }]; + }); + + await expect( + runTestFile( + { + file: 'test.yaml', + }, + {} + ) + ).rejects.toThrowError('Dependent workflows has failed steps'); + }, 8000); + + it('should throw an error when sourcedescription OpenAPI file does not exist', async () => { + const mockDocument = makeDocumentFromString( + JSON.stringify({ + arazzo: '1.0.1', + info: { + title: 'Cat Facts API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/not-existing.yaml', + }, + ], + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + ], + }, + ], + }), + 'api-test-framework/test.yml' + ); + + (readYaml as jest.Mock).mockResolvedValue(mockDocument.parsed); + (lint as jest.Mock).mockResolvedValueOnce([]); + (bundle as jest.Mock).mockResolvedValueOnce({ + bundle: { + parsed: mockDocument.parsed, + }, + }); + await expect(runTestFile({ file: 'test.yaml' }, {})).rejects.toThrowError( + `Could not find source description file 'api-samples/not-existing.yaml' at path 'test.yaml'` + ); + }); + + it('should throw an error when sourcedescription Arazzo file does not exist', async () => { + const mockDocument = makeDocumentFromString( + JSON.stringify({ + arazzo: '1.0.1', + info: { + title: 'Cat Facts API', + version: '1.0', + }, + sourceDescriptions: [ + { + name: 'cats', + type: 'openapi', + url: 'api-samples/cats.yaml', + }, + { + name: 'cats', + type: 'arazzo', + url: 'api-samples/not-existing-arazzo.yaml', + }, + ], + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [ + { + stepId: 'delete-step', + 'x-operation': { + url: 'http://localhost:3000/delete-mock', + method: 'delete', + }, + successCriteria: [ + { + condition: '$statusCode == 204', + }, + ], + }, + ], + }, + ], + }), + 'api-test-framework/test.yml' + ); + + (readYaml as jest.Mock).mockResolvedValue(mockDocument.parsed); + (lint as jest.Mock).mockResolvedValueOnce([]); + (bundle as jest.Mock).mockResolvedValueOnce({ + bundle: { + parsed: mockDocument.parsed, + }, + }); + await expect(runTestFile({ file: 'test.yaml' }, {})).rejects.toThrowError( + `Could not find source description file 'not-existing-arazzo.yaml' at path 'api-samples/not-existing-arazzo.yaml'` + ); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/schema/schema-checker.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/schema/schema-checker.test.ts new file mode 100644 index 0000000000..42e64045fb --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/schema/schema-checker.test.ts @@ -0,0 +1,505 @@ +import type { StepCallContext, TestContext } from '../../../../types'; + +import { CHECKS, checkSchema } from '../../../flow-runner'; +import { DEFAULT_SEVERITY_CONFIGURATION } from '../../../checks/severity'; + +describe('checkSchema', () => { + const stepCallCtx = { + $request: { + header: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + $response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Asian', + country: 'developed in the United Kingdom (founding stock from Asia)', + origin: '', + coat: 'Short', + pattern: 'Evenly solid', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + statusCode: 200, + header: { 'content-type': 'application/json' }, + contentType: 'application/json', + }, + $outputs: {}, + } as unknown as StepCallContext; + + const descriptionOperation = { + tags: ['Breeds'], + summary: 'Get a list of breeds', + description: 'Returns a a list of breeds', + operationId: 'getBreeds', + parameters: [ + { + name: 'limit', + in: 'query', + description: 'limit the amount of results returned', + required: false, + schema: { + type: 'integer', + format: 'int64', + }, + }, + ], + responses: { + '200': { + description: 'successful operation', + content: { + 'application/json': { + schema: { + type: 'array', + items: { + title: 'Breed model', + description: 'Breed', + properties: { + breed: { + title: 'Breed', + description: 'Breed', + type: 'string', + format: 'string', + }, + country: { + title: 'Country', + description: 'Country', + type: 'string', + format: 'string', + }, + origin: { + title: 'Origin', + description: 'Origin', + type: 'string', + format: 'string', + }, + coat: { + title: 'Coat', + description: 'Coat', + type: 'string', + format: 'string', + }, + pattern: { + title: 'Pattern', + description: 'Pattern', + type: 'string', + format: 'string', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + path: '/breeds', + method: 'get', + descriptionName: 'cats', + }; + + const ctx = { + severity: DEFAULT_SEVERITY_CONFIGURATION, + } as unknown as TestContext; + + it('should check expectation from test case description', () => { + const result = checkSchema({ + stepCallCtx, + descriptionOperation, + ctx, + }); + expect(result).toEqual([ + { + additionalMessage: 'Response code 200 matches one of description codes: [200]', + message: expect.stringContaining( + 'List of valid response codes are inferred from description' + ), + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + severity: 'error', + }, + { + message: 'Content type "application/json" is described in the schema.', + name: CHECKS.CONTENT_TYPE_CHECK, + pass: true, + severity: 'error', + }, + { + message: expect.stringMatching(/TYPE[\s\S]*must be array/i), + name: CHECKS.SCHEMA_CHECK, + pass: false, + severity: 'error', + }, + ]); + }); + + it('should check status code and schema from description', () => { + const result = checkSchema({ + stepCallCtx, + descriptionOperation, + ctx, + }); + + expect(result).toEqual([ + { + additionalMessage: 'Response code 200 matches one of description codes: [200]', + message: expect.stringContaining('200'), + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + severity: 'error', + }, + { + message: 'Content type "application/json" is described in the schema.', + name: CHECKS.CONTENT_TYPE_CHECK, + pass: true, + severity: 'error', + }, + { + message: expect.stringMatching(/TYPE[\s\S]*must be array/i), + name: CHECKS.SCHEMA_CHECK, + pass: false, + severity: 'error', + }, + ]); + }); + + it('should check ajv errors', () => { + const result = checkSchema({ + stepCallCtx: { + $request: { + header: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + $response: { + body: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Asian', + country: 'developed in the United Kingdom (founding stock from Asia)', + origin: '', + coat: 'Short', + pattern: 'Evenly solid', + }, + ], + statusCode: 200, + header: new Headers({ 'content-type': 'application/json' }), + contentType: 'application/json', + }, + $outputs: {}, + } as unknown as StepCallContext, + descriptionOperation, + ctx, + }); + + expect(result).toEqual([ + { + additionalMessage: 'Response code 200 matches one of description codes: [200]', + message: expect.stringContaining('200'), + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + severity: 'error', + }, + { + message: 'Content type "application/json" is described in the schema.', + name: CHECKS.CONTENT_TYPE_CHECK, + pass: true, + severity: 'error', + }, + { + message: '', + name: CHECKS.SCHEMA_CHECK, + pass: true, + severity: 'error', + }, + ]); + }); + + it('should check circular referenced schema', () => { + jest.spyOn(JSON, 'stringify').mockImplementationOnce(() => { + throw new Error('circular reference'); + }); + const result = checkSchema({ + stepCallCtx: { + $request: { + header: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + $response: { + body: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Asian', + country: 'developed in the United Kingdom (founding stock from Asia)', + origin: '', + coat: 'Short', + pattern: 'Evenly solid', + }, + ], + statusCode: 200, + header: new Headers({ 'content-type': 'application/json' }), + contentType: 'application/json', + }, + $outputs: {}, + } as unknown as StepCallContext, + descriptionOperation, + ctx, + }); + + expect(result).toEqual([ + { + additionalMessage: 'Response code 200 matches one of description codes: [200]', + message: expect.stringContaining( + 'List of valid response codes are inferred from description' + ), + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + severity: 'error', + }, + { + message: 'Content type "application/json" is described in the schema.', + name: CHECKS.CONTENT_TYPE_CHECK, + pass: true, + severity: 'error', + }, + ]); + + jest.restoreAllMocks(); + }); + + it('should catch ajvStrict.validate error', () => { + jest + .spyOn(require('@redocly/ajv/dist/2020').prototype, 'validate') + .mockImplementationOnce(() => { + throw new Error('ajvStrict.validate error'); + }); + + const result = checkSchema({ + stepCallCtx: { + $request: { + header: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + $response: { + body: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Asian', + country: 'developed in the United Kingdom (founding stock from Asia)', + origin: '', + coat: 'Short', + pattern: 'Evenly solid', + }, + ], + statusCode: 200, + header: new Headers({ 'content-type': 'application/json' }), + contentType: 'application/json', + }, + $outputs: {}, + } as unknown as StepCallContext, + descriptionOperation, + ctx, + }); + + expect(result).toEqual([ + { + additionalMessage: 'Response code 200 matches one of description codes: [200]', + message: expect.stringContaining( + 'List of valid response codes are inferred from description' + ), + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + severity: 'error', + }, + { + message: 'Content type "application/json" is described in the schema.', + name: CHECKS.CONTENT_TYPE_CHECK, + pass: true, + severity: 'error', + }, + { + message: 'Ajv error: ajvStrict.validate error', + name: CHECKS.SCHEMA_CHECK, + pass: false, + severity: 'error', + }, + ]); + + jest.restoreAllMocks(); + }); + + it('should return empty checks if no response available', () => { + const stepCtx = { + $request: { + header: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + $response: undefined, + $outputs: {}, + } as unknown as StepCallContext; + + const result = checkSchema({ + stepCallCtx: stepCtx, + descriptionOperation, + ctx, + }); + + expect(result).toEqual([]); + }); + + it('should check content type from description', () => { + const stepCallCtx = { + $request: { + header: {}, + path: '/breeds', + url: 'https://catfact.ninja/', + method: 'get', + queryParams: {}, + pathParams: {}, + headerParams: {}, + }, + $response: { + body: { + current_page: 1, + data: [ + { + breed: 'Abyssinian', + country: 'Ethiopia', + origin: 'Natural/Standard', + coat: 'Short', + pattern: 'Ticked', + }, + { + breed: 'Asian', + country: 'developed in the United Kingdom (founding stock from Asia)', + origin: '', + coat: 'Short', + pattern: 'Evenly solid', + }, + ], + first_page_url: 'https://catfact.ninja/breeds?page=1', + from: 1, + last_page: 4, + last_page_url: 'https://catfact.ninja/breeds?page=4', + links: [ + { + url: null, + label: 'Previous', + active: false, + }, + { + url: 'https://catfact.ninja/breeds?page=1', + label: '1', + active: true, + }, + ], + next_page_url: 'https://catfact.ninja/breeds?page=2', + path: 'https://catfact.ninja/breeds', + per_page: 25, + prev_page_url: null, + to: 25, + total: 98, + }, + statusCode: 200, + header: { 'content-type': 'application/text' }, + contentType: 'application/text', + }, + $outputs: {}, + } as unknown as StepCallContext; + + const result = checkSchema({ + stepCallCtx, + descriptionOperation, + ctx, + }); + + expect(result).toEqual([ + { + additionalMessage: 'Response code 200 matches one of description codes: [200]', + message: expect.stringContaining('200'), + name: CHECKS.STATUS_CODE_CHECK, + pass: true, + severity: 'error', + }, + { + message: expect.stringContaining('response is not described in the schema.'), + name: CHECKS.CONTENT_TYPE_CHECK, + pass: false, + severity: 'error', + }, + ]); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/success-criteria/check-success-criteria.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/success-criteria/check-success-criteria.test.ts new file mode 100644 index 0000000000..ce55a95c7a --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/success-criteria/check-success-criteria.test.ts @@ -0,0 +1,655 @@ +import { Headers } from 'undici'; + +import type { TestContext, Step, RegexpSuccessCriteria } from '../../../../types'; + +import { checkCriteria } from '../../../flow-runner/success-criteria/check-success-criteria'; +import { CHECKS, DEFAULT_SEVERITY_CONFIGURATION } from '../../../checks'; + +describe('checkSuccessCriteria', () => { + const stepMock: Step = { + stepId: 'stepId', + 'x-operation': { + method: 'get', + url: 'http://localhost:3000/some/path', + }, + checks: [], + response: { + body: {}, + statusCode: 200, + header: {}, + contentType: 'application/json', + }, + }; + it('should return empty array if successCriteria is empty', () => { + const result = checkCriteria({ + workflowId: 'workflowId', + step: stepMock, + criteria: [], + ctx: { + arazzo: 'some spec', + info: { + title: 'some title', + version: 'some version', + description: 'some description', + }, + workflows: [], + $workflows: { + workflowId: { + steps: { + stepId: { + response: { + body: {}, + code: 200, + headers: new Headers(), + contentType: 'application/json', + }, + request: { + queryParams: {}, + pathParams: {}, + headerParams: {}, + url: '', + path: '', + method: '', + headers: { + 'content-type': 'application/json', + }, + body: {}, + }, + }, + }, + }, + }, + descriptions: '', + env: {}, + requests: {}, + responses: {}, + harLogs: [], + severity: DEFAULT_SEVERITY_CONFIGURATION, + } as unknown as TestContext, + }); + expect(result).toEqual([]); + }); + + it('should return empty array if successCriteria is undefined', () => { + const result = checkCriteria({ + workflowId: 'workflowId', + step: stepMock, + ctx: { + arazzo: 'some spec', + info: { + title: 'some title', + version: 'some version', + description: 'some description', + }, + workflows: [], + $workflows: { + workflowId: { + steps: { + stepId: { + response: { + body: {}, + code: 200, + headers: new Headers(), + contentType: 'application/json', + }, + request: { + queryParams: {}, + pathParams: {}, + headerParams: {}, + url: '', + path: '', + method: '', + headers: { + 'content-type': 'application/json', + }, + body: {}, + }, + }, + }, + }, + }, + descriptions: '', + env: {}, + requests: {}, + responses: {}, + harLogs: [], + } as unknown as TestContext, + }); + expect(result).toEqual([]); + }); + + it('should process regex success criteria', () => { + const stepMock: Step = { + stepId: 'stepId', + 'x-operation': { + method: 'get', + url: 'http://localhost:3000/some/path', + }, + checks: [], + response: { + body: { + slug: 'organization-1', + name: 'respect-test-project-name', + }, + statusCode: 200, + header: {}, + contentType: 'application/json', + }, + }; + + const result = checkCriteria({ + workflowId: 'workflowId', + step: stepMock, + criteria: [ + { + type: 'regex', + context: '$statusCode', + condition: '^200$', + }, + { + type: 'regex', + context: '$response.body#/slug', + condition: '/^organization-1/i', + }, + { + type: 'regex', + context: '$response.body#/name', + condition: '/respect-test-project-name/', + }, + ], + ctx: { + arazzo: 'some spec', + info: { + title: 'some title', + version: 'some version', + description: 'some description', + }, + workflows: [], + $workflows: { + workflowId: { + steps: { + stepId: { + response: { + body: { + slug: 'organization-1', + }, + code: 200, + headers: new Headers(), + contentType: 'application/json', + }, + request: { + queryParams: {}, + pathParams: {}, + headerParams: {}, + url: '', + path: '', + method: '', + headers: { + 'content-type': 'application/json', + }, + body: {}, + }, + }, + }, + }, + }, + descriptions: '', + env: {}, + requests: {}, + responses: {}, + harLogs: [], + severity: DEFAULT_SEVERITY_CONFIGURATION, + } as unknown as TestContext, + }); + expect(result).toEqual([ + { + message: + 'Checking regex criteria: {"type":"regex","context":"$statusCode","condition":"^200$"}', + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + severity: 'error', + }, + { + message: + 'Checking regex criteria: {"type":"regex","context":"$response.body#/slug","condition":"/^organization-1/i"}', + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + severity: 'error', + }, + { + message: + 'Checking regex criteria: {"type":"regex","context":"$response.body#/name","condition":"/respect-test-project-name/"}', + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + severity: 'error', + }, + ]); + }); + + it('should process simple success criteria', () => { + const stepMock: Step = { + stepId: 'stepId', + 'x-operation': { + method: 'get', + url: 'http://localhost:3000/some/path', + }, + checks: [], + response: { + body: { + items: [ + { + sourceId: 'rem_00h3hbrz9cce4drnctvtx62rzr', + }, + { + sourceId: 'rem_00h3hbrz9cce4drnctvtx62rzr', + }, + ], + }, + statusCode: 200, + header: {}, + contentType: 'application/json', + }, + }; + const result = checkCriteria({ + workflowId: 'workflowId', + step: stepMock, + criteria: [ + { + condition: '$statusCode == 200', + }, + { + condition: '$response.body#/items/0/sourceId == "rem_00h3hbrz9cce4drnctvtx62rzr"', + }, + ], + ctx: { + arazzo: 'some spec', + info: { + title: 'some title', + version: 'some version', + description: 'some description', + }, + workflows: [], + $workflows: { + workflowId: { + steps: { + stepId: { + response: { + body: {}, + code: 200, + headers: new Headers(), + contentType: 'application/json', + }, + request: { + queryParams: {}, + pathParams: {}, + headerParams: {}, + url: '', + path: '', + method: '', + headers: { + 'content-type': 'application/json', + }, + body: {}, + }, + }, + }, + }, + }, + descriptions: '', + env: {}, + requests: {}, + responses: {}, + harLogs: [], + severity: DEFAULT_SEVERITY_CONFIGURATION, + } as unknown as TestContext, + }); + expect(result).toEqual([ + { + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + severity: 'error', + }, + { + message: + 'Checking simple criteria: {"condition":"$response.body#/items/0/sourceId == \\"rem_00h3hbrz9cce4drnctvtx62rzr\\""}', + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + severity: 'error', + }, + ]); + }); + + it('should fail JSONPath success criteria', () => { + const result = checkCriteria({ + workflowId: 'workflowId', + step: stepMock, + criteria: [ + { + type: 'jsonpath', + context: '$response.body', + condition: '$.pets[?(@.length>3)] && $.access_token != null', + }, + ], + ctx: { + workflows: [], + $workflows: { + workflowId: { + steps: { + stepId: { + response: { + body: { + pets: [{ name: 'cat' }, { name: 'bunny' }], + access_token: 'some token', + }, + code: 200, + headers: new Headers(), + contentType: 'application/json', + }, + request: { + queryParams: {}, + pathParams: {}, + headerParams: {}, + url: '', + path: '', + method: '', + headers: { + 'content-type': 'application/json', + }, + body: {}, + }, + }, + }, + }, + }, + descriptions: '', + $response: { + body: { + pets: [{ name: 'cat' }, { name: 'bunny' }], + access_token: 'some token', + }, + }, + severity: DEFAULT_SEVERITY_CONFIGURATION, + } as unknown as TestContext, + }); + expect(result).toEqual([ + { + message: 'Checking jsonpath criteria: $.pets[?(@.length>3)] && $.access_token != null', + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: false, + severity: 'error', + }, + ]); + }); + + it('should pass jsonpath success criteria', () => { + const stepMock: Step = { + stepId: 'stepId', + 'x-operation': { + method: 'get', + url: 'http://localhost:3000/some/path', + }, + checks: [], + response: { + body: { pets: ['dog', 'cat', 'bunny'], access_token: null, checks: [] }, + statusCode: 200, + header: {}, + contentType: 'application/json', + }, + }; + const result = checkCriteria({ + workflowId: 'workflowId', + step: stepMock, + criteria: [ + { + type: { + type: 'jsonpath', + version: 'draft-goessner-dispatch-jsonpath-00', + }, + context: '$response.body', + condition: '$.pets.length > 0', + }, + { + type: { + type: 'jsonpath', + version: 'draft-goessner-dispatch-jsonpath-00', + }, + context: '$response.body', + condition: '$.checks.length == 0', + }, + ], + ctx: { + workflows: [], + $workflows: { + workflowId: { + steps: { + stepId: { + response: { + body: { pets: ['dog', 'cat', 'bunny'], access_token: null }, + code: 200, + headers: new Headers(), + contentType: 'application/json', + }, + request: { + queryParams: {}, + pathParams: {}, + headerParams: {}, + url: '', + path: '', + method: '', + headers: { + 'content-type': 'application/json', + }, + body: {}, + }, + }, + }, + }, + }, + descriptions: '', + $response: { + body: { pets: ['dog', 'cat', 'bunny'], access_token: null }, + }, + severity: DEFAULT_SEVERITY_CONFIGURATION, + } as unknown as TestContext, + }); + expect(result).toEqual([ + { + message: 'Checking jsonpath criteria: $.pets.length > 0', + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + severity: 'error', + }, + { + message: 'Checking jsonpath criteria: $.checks.length == 0', + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: true, + severity: 'error', + }, + ]); + }); + + it('should fail jsonpath success criteria when context is missing', () => { + const result = checkCriteria({ + workflowId: 'workflowId', + step: stepMock, + criteria: [ + { + type: { + type: 'jsonpath', + version: 'draft-goessner-dispatch-jsonpath-00', + }, + context: '$response.body', + condition: '$.pets.length > 2', + }, + ], + ctx: { + workflows: [], + $workflows: { + workflowId: { + steps: { + stepId: { + response: { + body: { access_token: null }, + code: 200, + headers: new Headers(), + contentType: 'application/json', + }, + request: { + queryParams: {}, + pathParams: {}, + headerParams: {}, + url: '', + path: '', + method: '', + headers: { + 'content-type': 'application/json', + }, + body: {}, + }, + }, + }, + }, + }, + descriptions: '', + $response: { + body: { access_token: null }, + }, + severity: DEFAULT_SEVERITY_CONFIGURATION, + } as unknown as TestContext, + }); + expect(result).toEqual([ + { + message: 'Checking jsonpath criteria: $.pets.length > 2', + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: false, + severity: 'error', + }, + ]); + }); + + it('should return failed check', () => { + const result = checkCriteria({ + workflowId: 'workflowId', + step: stepMock, + criteria: [ + { + condition: '$statusCode === () => {throw new Error("error")}', + type: 'not_exist', + } as unknown as RegexpSuccessCriteria, + ], + ctx: { + arazzo: 'some spec', + info: { + title: 'some title', + version: 'some version', + description: 'some description', + }, + workflows: [], + $workflows: { + workflowId: { + steps: { + stepId: { + response: { + body: {}, + code: 200, + headers: new Headers(), + contentType: 'application/json', + }, + request: { + queryParams: {}, + pathParams: {}, + headerParams: {}, + url: '', + path: '', + method: '', + headers: { + 'content-type': 'application/json', + }, + body: {}, + }, + }, + }, + }, + }, + descriptions: '', + env: {}, + requests: {}, + responses: {}, + harLogs: [], + severity: DEFAULT_SEVERITY_CONFIGURATION, + } as unknown as TestContext, + }); + expect(result).toEqual([ + { + message: + 'Failed to pass {"condition":"$statusCode === () => {throw new Error(\\"error\\")}","type":"not_exist"}: Runtime expression is not valid: $statusCode === () => {throw new Error("error")}', + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: false, + severity: 'error', + }, + ]); + }); + + it('should return failed check if workflowId is undefined', () => { + const result = checkCriteria({ + workflowId: undefined, + step: stepMock, + criteria: [ + { + condition: '$statusCode === () => {throw new Error("error")}', + type: 'not_exist', + } as unknown as RegexpSuccessCriteria, + ], + ctx: { + arazzo: 'some spec', + info: { + title: 'some title', + version: 'some version', + description: 'some description', + }, + workflows: [], + $workflows: { + workflowId: { + steps: { + stepId: { + response: { + body: {}, + code: 200, + headers: new Headers(), + contentType: 'application/json', + }, + request: { + queryParams: {}, + pathParams: {}, + headerParams: {}, + url: '', + path: '', + method: '', + headers: { + 'content-type': 'application/json', + }, + body: {}, + }, + }, + }, + }, + }, + descriptions: '', + env: {}, + requests: {}, + responses: {}, + harLogs: [], + severity: DEFAULT_SEVERITY_CONFIGURATION, + } as unknown as TestContext, + }); + expect(result).toEqual([ + { + message: 'Undefined workflowId for step stepId', + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: false, + severity: 'error', + }, + ]); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/success-criteria/validate-success-criteria.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/success-criteria/validate-success-criteria.test.ts new file mode 100644 index 0000000000..cc0cc359d4 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/flow-runner/success-criteria/validate-success-criteria.test.ts @@ -0,0 +1,139 @@ +import { validateSuccessCriteria, isRegexpSuccessCriteria } from '../../../flow-runner'; + +describe('validateSuccessCriteria', () => { + describe('Simple criteria', () => { + it('throws an error if an unexpected key is present', () => { + expect(() => { + validateSuccessCriteria([ + { + condition: 'status === 400', + }, + { + condition: '3.14 === 3.14', + }, + ]); + }).not.toThrow(); + }); + + it('throws an error if an unknown key is present', () => { + expect(() => { + validateSuccessCriteria([ + { + condition: '$name === 400', + }, + ]); + }).toThrowError(`Success criteria condition $name === 400 is not allowed.`); + }); + + it('does not throw an error if all keys are expected', () => { + expect(() => { + validateSuccessCriteria([ + { + condition: '$statusCode === 400', + }, + ]); + }).not.toThrow(); + }); + }); + describe('RegexpSuccessCriteria', () => { + it('throws an error if not allowed key is present', () => { + expect(() => { + validateSuccessCriteria([ + { + type: 'regex', + context: '$someUnknownKey', + condition: 'status === 400', + }, + ]); + }).toThrowError(`Success criteria context "$someUnknownKey" is not allowed.`); + }); + + it('throws an error if an unexpected key is present', () => { + expect(() => { + validateSuccessCriteria([ + { + type: 'regex', + context: 'UnknownKey', + condition: 'status === 400', + }, + ]); + }).toThrowError(`"UnknownKey" does not contain any valid context.`); + }); + + it('does not throw an error if all keys are expected', () => { + expect(() => { + validateSuccessCriteria([ + { + type: 'regex', + context: '$statusCode', + condition: '^200$', + }, + ]); + }).not.toThrow(); + }); + }); + describe('jsonpath', () => { + it('throws an error if context is missing', () => { + expect(() => { + validateSuccessCriteria([ + { + type: 'jsonpath', + condition: '$.pets[?(@.length>3)] || $.access_token != null', + }, + ]); + }).toThrowError(`jsonpath success criteria context is required.`); + }); + + it('throws an error if condition is missing', () => { + expect(() => { + validateSuccessCriteria([ + { + type: 'jsonpath', + context: '$response.body', + condition: '', + }, + ]); + }).toThrowError(`jsonpath success criteria condition is required.`); + }); + + it('does not throw an error if all keys are expected', () => { + expect(() => { + validateSuccessCriteria([ + { + type: { + type: 'jsonpath', + version: 'draft-goessner-dispatch-jsonpath-00', + }, + context: '$response.body', + condition: '$.pets[?(@.length>3)] || $.access_token != null', + }, + ]); + }).not.toThrow(); + }); + }); +}); + +describe('isRegexpSuccessCriteria', () => { + it('returns true if the criteria is a RegexpSuccessCriteria', () => { + expect( + isRegexpSuccessCriteria({ + type: 'regex', + context: '$statusCode', + condition: '^200$', + }) + ).toBe(true); + }); + + it('returns false if the criteria is not a RegexpSuccessCriteria', () => { + expect( + isRegexpSuccessCriteria({ + type: { + type: 'jsonpath', + version: 'draft-goessner-dispatch-jsonpath-00', + }, + context: '$statusCode', + condition: '200', + }) + ).toBe(false); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/runtime-expressions/evaluate.test.ts b/packages/respect-core/src/modules/__tests__/runtime-expressions/evaluate.test.ts new file mode 100644 index 0000000000..dd47e6fd39 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/runtime-expressions/evaluate.test.ts @@ -0,0 +1,623 @@ +import type { RuntimeExpressionContext } from '../../../types'; + +import { createFaker } from '../../faker'; +import { + evaluateRuntimeExpressionPayload, + evaluateRuntimeExpression, +} from '../../runtime-expressions'; + +const faker = createFaker(); +const runtimeExpressionContext = { + $workflows: { + workflow1: { + $steps: { + step1: { + $outputs: { + output1: 'output1Value', + }, + request: { + header: { + accept: 'application/json, application/problem+json', + authorization: 'Basic Og==', + 'content-type': 'application/json', + }, + path: {}, + query: {}, + url: 'https://redocly.com/_mock/docs/openapi/museum-api//museum-hours', + method: 'get', + }, + response: { + body: [ + { + date: '2021-09-01', + timeOpen: '09:00', + timeClose: '17:00', + }, + ], + statusCode: 200, + header: { + 'content-type': 'application/json', + age: '0', + 'x-frame-options': 'deny', + }, + path: {}, + query: {}, + }, + }, + step2: { + outputs: { + ticketId: '382c0820-0530-4f4b-99af-13811ad0f17a', + }, + }, + 'step-three': { + outputs: { + hardcoded: '125', + name: 'close', + createdEventId: 'dad4bce8-f5cb-4078-a211-995864315e39', + fullBody: { + eventid: 'dad4bce8-f5cb-4078-a211-995864315e39', + name: 'Mermaid Treasure Identification and Analysis', + location: 'Under the seaaa 🦀 🎶 🌊.', + eventdescription: + 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.', + dates: ['2023-12-15', '2023-12-22'], + price: 0, + }, + }, + request: { + header: { + 'content-type': 'application/json', + accept: 'application/json, application/problem+json', + authorization: 'Basic Og==', + }, + body: { + test: '382c0820-0530-4f4b-99af-13811ad0f17a', + name: 'Mermaid Treasure Identification and Analysis', + location: 'Under the seaaa 🦀 🎶 🌊.', + eventDescription: + 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.', + dates: ['2023-12-15', '2023-12-22'], + price: 0, + booleanValue: true, + multiwordSecret: 'Bearer 382c0820-0530-4f4b-99af-13811ad0f17a', + }, + path: {}, + query: {}, + url: 'https://redocly.com/_mock/docs/openapi/museum-api//special-events', + method: 'post', + }, + response: { + body: { + eventId: 'dad4bce8-f5cb-4078-a211-995864315e39', + name: 'Mermaid Treasure Identification and Analysis', + location: 'Under the seaaa 🦀 🎶 🌊.', + eventDescription: + 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.', + dates: ['2023-12-15', '2023-12-22'], + price: 0, + }, + statusCode: 201, + header: { + 'content-type': 'application/json', + 'x-xss-protection': '1;mode=block;', + }, + query: {}, + path: {}, + requestHeaders: { + 'content-type': 'application/json', + accept: 'application/json, application/problem+json', + authorization: 'Basic Og==', + }, + }, + }, + }, + outputs: { + bodyCopy: { + name: 'Mermaid Treasure Identification and Analysis', + }, + }, + }, + }, + $sourceDescriptions: {}, + $faker: faker, + $steps: { + step1: { + outputs: { + output1: 'output1Value', + bodyCopy: { + name: 'Mermaid Treasure Identification and Analysis', + }, + }, + request: { + header: { + accept: 'application/json, application/problem+json', + authorization: 'Basic Og==', + 'content-type': 'application/json', + }, + path: {}, + query: {}, + url: 'https://redocly.com/_mock/docs/openapi/museum-api//museum-hours', + method: 'get', + }, + response: { + body: [ + { + date: '2021-09-01', + timeOpen: '09:00', + timeClose: '17:00', + }, + ], + statusCode: 200, + header: { + 'content-type': 'application/json', + age: '0', + 'x-frame-options': 'deny', + }, + path: {}, + query: {}, + }, + }, + 'step-three': { + outputs: { + hardcoded: '125', + name: 'close', + createdEventId: 'dad4bce8-f5cb-4078-a211-995864315e39', + fullBody: { + eventid: 'dad4bce8-f5cb-4078-a211-995864315e39', + name: 'Mermaid Treasure Identification and Analysis', + location: 'Under the seaaa 🦀 🎶 🌊.', + eventdescription: + 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.', + dates: ['2023-12-15', '2023-12-22'], + price: 0, + }, + }, + request: { + header: { + 'content-type': 'application/json', + accept: 'application/json, application/problem+json', + authorization: 'Basic Og==', + }, + body: { + test: '382c0820-0530-4f4b-99af-13811ad0f17a', + name: 'Mermaid Treasure Identification and Analysis', + location: 'Under the seaaa 🦀 🎶 🌊.', + eventDescription: + 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.', + dates: ['2023-12-15', '2023-12-22'], + price: 0, + booleanValue: true, + multiwordSecret: 'Bearer 382c0820-0530-4f4b-99af-13811ad0f17a', + }, + path: {}, + query: {}, + url: 'https://redocly.com/_mock/docs/openapi/museum-api//special-events', + method: 'post', + }, + response: { + body: { + eventId: 'dad4bce8-f5cb-4078-a211-995864315e39', + name: 'Mermaid Treasure Identification and Analysis', + location: 'Under the seaaa 🦀 🎶 🌊.', + eventDescription: + 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.', + dates: ['2023-12-15', '2023-12-22'], + price: 0, + }, + statusCode: 201, + header: { + connection: 'close', + 'content-type': 'application/json', + date: 'Thu, 31 Oct 2024 09:25:29 GMT', + }, + query: {}, + path: {}, + requestHeaders: { + 'content-type': 'application/json', + accept: 'application/json, application/problem+json', + authorization: 'Basic Og==', + }, + }, + }, + }, + $response: { + body: { + eventId: 'dad4bce8-f5cb-4078-a211-995864315e39', + name: 'Mermaid Treasure Identification and Analysis', + location: 'Under the seaaa 🦀 🎶 🌊.', + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs.', + dates: ['2023-12-15', '2023-12-22'], + price: 0, + items: [], + device_code: '123', + piNumber: 3.14, + }, + statusCode: 201, + header: { + 'content-type': 'application/json', + date: 'Thu, 31 Oct 2024 09:25:29 GMT', + server: 'Caddy', + }, + query: {}, + path: {}, + }, + $request: { + header: { + 'content-type': 'application/json', + accept: 'application/json, application/problem+json', + authorization: 'Basic Og==', + }, + body: { + test: '382c0820-0530-4f4b-99af-13811ad0f17a', + name: 'Mermaid Treasure Identification and Analysis', + location: 'Under the seaaa 🦀 🎶 🌊.', + eventDescription: + 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.', + dates: ['2023-12-15', '2023-12-22'], + price: 0, + booleanValue: true, + multiwordSecret: 'Bearer 382c0820-0530-4f4b-99af-13811ad0f17a', + }, + path: {}, + query: {}, + }, + $outputs: { + bodyCopy: { + name: 'Mermaid Treasure Identification and Analysis', + }, + }, + $inputs: { + env: { + AUTH_TOKEN: 'secret', + }, + }, + $components: {}, + $url: 'http://example.com', + $method: 'get', + $statusCode: 200, +} as unknown as RuntimeExpressionContext; + +describe('evaluateRuntimeExpressionPayload', () => { + it('should evaluate string value', () => { + const payload = 'foo'; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual('foo'); + }); + + it('should evaluate number value', () => { + const payload = 32; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual(32); + }); + + it('should evaluate boolead value', () => { + const payload = true; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual(true); + }); + + it('should evaluate simple runtime expression value', () => { + const payload = '$statusCode'; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual(200); + }); + + it('should evaluate simple runtime expression value with dash', () => { + const payload = '$request.header.content-type'; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual('application/json'); + }); + it('should evaluate simple runtime expression value with dot notation', () => { + const payload = '$steps.step-three.outputs.fullBody.dates.0'; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual('2023-12-15'); + }); + + it('should evaluate $faker runtime expression value', () => { + const payload = '$faker.number.integer({ min: 1, max: 10 })'; + expect( + typeof evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toBe('number'); + }); + + it('should evaluate $faker inside string runtime expression value', () => { + const payload = 'Some text {$faker.number.integer({ min: 1, max: 10 })}'; + expect( + typeof evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toBe('string'); + }); + + it('should evaluate multiword runtime expression value', () => { + const payload = 'Bearer {$steps.step-three.outputs.hardcoded}'; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual('Bearer 125'); + }); + + it('should evaluate multiword runtime expression with JsonPointer value', () => { + const payload = 'Bearer {$request.body#/multiwordSecret}'; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual('Bearer Bearer 382c0820-0530-4f4b-99af-13811ad0f17a'); + }); + + it('should evaluate xml with runtime expression values', () => { + const payload = ` + + + {$steps.step-three.response.body.location} + {$steps.step-three.response.body.name} + Usa + `; + expect(evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext })) + .toEqual(` + + + Under the seaaa 🦀 🎶 🌊. + Mermaid Treasure Identification and Analysis + Usa + `); + }); + + it('should evaluate x-www-form-urlencoded string with runtime expression values', () => { + const payload = + 'client_id={$steps.step-three.response.body.location}&grant_type={$steps.step-three.response.body.location}'; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual('client_id=Under the seaaa 🦀 🎶 🌊.&grant_type=Under the seaaa 🦀 🎶 🌊.'); + }); + + it('should evaluate object runtime expression value', () => { + const payload = '$response.body'; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual({ + eventId: 'dad4bce8-f5cb-4078-a211-995864315e39', + name: 'Mermaid Treasure Identification and Analysis', + location: 'Under the seaaa 🦀 🎶 🌊.', + device_code: '123', + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs.', + dates: ['2023-12-15', '2023-12-22'], + price: 0, + items: [], + piNumber: 3.14, + }); + }); + + it('should evaluate string runtime expression value', () => { + const payload = '$response.body == "some string value"'; + const runtimeExpressionContext = { + $response: { + body: 'some string value', + }, + } as unknown as RuntimeExpressionContext; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual(`some string value == \"some string value\"`); + }); + + it('should evaluate runctime expressions with url comparison', () => { + const payload = '$url == "http://example.com"'; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual(`http://example.com == \"http://example.com\"`); + }); + + it('should evaluate requestBody object with runtime expression values', () => { + const payload = { + test: '$response.body#/eventId', + name: '$response.body#/name', + dates: ['$steps.step-three.outputs.fullBody.dates.0'], + price: 0, + booleanValue: true, + multiwordSecret: 'Bearer {$inputs.env.AUTH_TOKEN}', + }; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual({ + test: 'dad4bce8-f5cb-4078-a211-995864315e39', + name: 'Mermaid Treasure Identification and Analysis', + dates: ['2023-12-15'], + price: 0, + booleanValue: true, + multiwordSecret: 'Bearer secret', + }); + }); + + it('should evaluate properties with underscore in runtime expression values', () => { + expect( + evaluateRuntimeExpressionPayload({ + payload: '$response.body#/device_code', + context: runtimeExpressionContext, + }) + ).toEqual('123'); + }); + + it('should evaluate multipart/form-data payload ', () => { + const payload = { + name: 'test', + singleFile: "$file('./test.yaml')", + multipleFiles: ["$file('./logo.png')", "$file('./image.svg')"], + }; + expect( + evaluateRuntimeExpressionPayload({ + payload, + context: runtimeExpressionContext, + contentType: 'multipart/form-data', + }) + ).toEqual({ + multipleFiles: ['./logo.png', './image.svg'], + name: 'test', + singleFile: './test.yaml', + }); + }); + + it('should evaluate payload with different outputs access variations', () => { + const payload = { + name: '$outputs.bodyCopy#/name', + name2: '$outputs.bodyCopy.name', + name3: '$workflows.workflow1.outputs.bodyCopy#/name', + name4: '$workflows.workflow1.outputs.bodyCopy.name', + name5: '$steps.step1.outputs.bodyCopy.name', + name6: '$steps.step1.outputs.bodyCopy#/name', + }; + expect( + evaluateRuntimeExpressionPayload({ payload, context: runtimeExpressionContext }) + ).toEqual({ + name: 'Mermaid Treasure Identification and Analysis', + name2: 'Mermaid Treasure Identification and Analysis', + name3: 'Mermaid Treasure Identification and Analysis', + name4: 'Mermaid Treasure Identification and Analysis', + name5: 'Mermaid Treasure Identification and Analysis', + name6: 'Mermaid Treasure Identification and Analysis', + }); + }); +}); + +describe('evaluateRuntimeExpression', () => { + it('should evaluate string value', () => { + const payload = 'foo'; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toEqual('foo'); + }); + + it('should evaluate number value', () => { + const payload = 32; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toEqual(32); + }); + + it('should evaluate boolead value', () => { + const payload = true; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toEqual(true); + }); + + it('should evaluate simple runtime expression value', () => { + const payload = '$statusCode'; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toEqual(200); + }); + + it('should evaluate simple runtime expression value with dash', () => { + const payload = '$request.header.content-type'; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toEqual( + 'application/json' + ); + }); + + it('should evaluate simple runtime expression with ! condition', () => { + const payload = '$request.header.content-type != "application/xml"'; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toEqual(true); + }); + + it('should evaluate simple runtime expression comparing undefined', () => { + const payload = '$request.body#/test != undefined'; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toEqual(true); + expect( + evaluateRuntimeExpression( + '$request.body#/test == "382c0820-0530-4f4b-99af-13811ad0f17a"', + runtimeExpressionContext + ) + ).toEqual(true); + expect( + evaluateRuntimeExpression('$request.body#/price == 0', runtimeExpressionContext) + ).toEqual(true); + }); + + it('should evaluate simple runtime expression comparing empty array', () => { + expect( + evaluateRuntimeExpression('$response.body#/items == []', runtimeExpressionContext) + ).toEqual(false); + }); + + it('should evaluate chained runtime expression value with dash', () => { + const payload = + '{$request.header.content-type == "application/json" && $statusCode == 200 || $steps.step-three.outputs.hardcoded != 125}'; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toEqual(true); + }); + + it('should evaluate simple runtime expression value with dot notation', () => { + const payload = '$steps.step-three.outputs.fullBody.dates.0'; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toEqual('2023-12-15'); + }); + + it('should evaluate $faker runtime expression value', () => { + const payload = '$faker.number.integer({ min: 1, max: 10 })'; + expect(typeof evaluateRuntimeExpression(payload, runtimeExpressionContext)).toBe('number'); + }); + + it('should evaluate $faker email runtime expression value', () => { + const payload = '$faker.string.email({provider:"gmail"})'; + expect(typeof evaluateRuntimeExpression(payload, runtimeExpressionContext)).toBe('string'); + }); + + it('should evaluate $faker runtime expression comparing numbers', () => { + const payload = '$faker.number.integer({ min: 1, max: 10 }) < 20'; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toBe(true); + }); + + it('should evaluate $faker inside string runtime expression value', () => { + const payload = 'Some text {$faker.number.integer({ min: 1, max: 10 })}'; + expect(typeof evaluateRuntimeExpression(payload, runtimeExpressionContext)).toBe('string'); + }); + + it('should evaluete list runtime expression value', () => { + const payload = ['foo', 'bar', '$statusCode']; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toEqual([ + 'foo', + 'bar', + 200, + ]); + }); + + it('should evaluate object runtime expression value', () => { + const payload = { + foo: 'bar', + bar: '$statusCode', + }; + expect(evaluateRuntimeExpression(payload, runtimeExpressionContext)).toEqual({ + foo: 'bar', + bar: 200, + }); + }); + + it('should evaluate runtime expression with case insensitive headers', () => { + const expression = + '$response.header.Content-Type == "application/json" && $response.header.coNTent-tYPe == "application/json"'; + expect(evaluateRuntimeExpression(expression, runtimeExpressionContext)).toEqual(true); + }); + + it('should evaluate runtime expression with outputs', () => { + const expression1 = '$outputs.bodyCopy#/name == "Mermaid Treasure Identification and Analysis"'; + const expression2 = + '$workflows.workflow1.outputs.bodyCopy#/name == "Mermaid Treasure Identification and Analysis"'; + const expression3 = + '$steps.step1.outputs.bodyCopy#/name == "Mermaid Treasure Identification and Analysis"'; + const expression4 = '$outputs.bodyCopy.name == "Mermaid Treasure Identification and Analysis"'; + const expression5 = + '$workflows.workflow1.outputs.bodyCopy.name == "Mermaid Treasure Identification and Analysis"'; + const expression6 = + '$steps.step1.outputs.bodyCopy.name == "Mermaid Treasure Identification and Analysis"'; + const expression7 = '$outputs.bodyCopy.name == $steps.step1.outputs.bodyCopy.name'; + + expect(evaluateRuntimeExpression(expression1, runtimeExpressionContext)).toEqual(true); + expect(evaluateRuntimeExpression(expression2, runtimeExpressionContext)).toEqual(true); + expect(evaluateRuntimeExpression(expression3, runtimeExpressionContext)).toEqual(true); + expect(evaluateRuntimeExpression(expression4, runtimeExpressionContext)).toEqual(true); + expect(evaluateRuntimeExpression(expression5, runtimeExpressionContext)).toEqual(true); + expect(evaluateRuntimeExpression(expression6, runtimeExpressionContext)).toEqual(true); + expect(evaluateRuntimeExpression(expression7, runtimeExpressionContext)).toEqual(true); + }); + + it('should evaluate runtime expression with float numbers', () => { + const expression1 = '3.14 == 3.14'; + const expression2 = '3.14 == $response.body#/piNumber'; + const expression3 = '$response.body#/piNumber > 3.14'; + expect(evaluateRuntimeExpression(expression1, runtimeExpressionContext)).toEqual(true); + expect(evaluateRuntimeExpression(expression2, runtimeExpressionContext)).toEqual(true); + expect(evaluateRuntimeExpression(expression3, runtimeExpressionContext)).toEqual(false); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/runtime-expressions/lint.test.ts b/packages/respect-core/src/modules/__tests__/runtime-expressions/lint.test.ts new file mode 100644 index 0000000000..c76d8cf746 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/runtime-expressions/lint.test.ts @@ -0,0 +1,240 @@ +import { lintExpression } from '../../runtime-expressions/lint'; + +describe('lintExpression', () => { + describe('general cases', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression('$statusCode == 201')).not.toThrow(); + expect(() => lintExpression('{$statusCode == 201 || $statusCode != 200}')).not.toThrow(); + expect(() => + lintExpression('$statusCode == 201 && $statusCode != 200 || $url == "test"') + ).not.toThrow(); + expect(() => + lintExpression('{$statusCode == 201 && $statusCode != 200 || $url == "test"}') + ).not.toThrow(); + expect(() => lintExpression('$response.header.Server == 201')).not.toThrow(); + expect(() => lintExpression('{$response.header.Server == 201}')).not.toThrow(); + expect(() => lintExpression('$response.body#/device_code')).not.toThrow(); + expect(() => lintExpression('3.14 == 3.14')).not.toThrow(); + expect(() => lintExpression('$response.body#/someFloat > 3.14')).not.toThrow(); + }); + + it('should throw an error if the expression is not valid', () => { + expect(() => lintExpression('foo bar')).toThrowError( + 'Runtime expression is not valid: foo bar' + ); + expect(() => + lintExpression("$response.body.name == 'Mermaid Treasure Identification and Analysis'") + ).toThrowError( + "Runtime expression is not valid: $response.body.name == 'Mermaid Treasure Identification and Analysis'" + ); + }); + }); + + describe('$url', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression('$url == "http://test.com"')).not.toThrow(); + }); + + it('should throw an error if the expression is not valid', () => { + expect(() => + lintExpression("$url.body.name == 'Mermaid Treasure Identification and Analysis'") + ).toThrowError( + "Runtime expression is not valid: $url.body.name == 'Mermaid Treasure Identification and Analysis'" + ); + }); + }); + + describe('$method', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression("$method == 'GET'")).not.toThrow(); + }); + + it('should throw an error if the expression is not valid', () => { + expect(() => + lintExpression("$method.body.name == 'Mermaid Treasure Identification and Analysis'") + ).toThrowError( + "Runtime expression is not valid: $method.body.name == 'Mermaid Treasure Identification and Analysis'" + ); + }); + }); + + describe('$statusCode', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression('$statusCode == 200')).not.toThrow(); + }); + + it('should throw an error if the expression is not valid', () => { + expect(() => + lintExpression("$statusCode.body.name == 'Mermaid Treasure Identification and Analysis'") + ).toThrowError( + "Runtime expression is not valid: $statusCode.body.name == 'Mermaid Treasure Identification and Analysis'" + ); + }); + }); + + describe('$request', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression('$request.body#/name != 42')).not.toThrow(); + expect(() => lintExpression("$request.header.accept == 'application/json'")).not.toThrow(); + expect(() => lintExpression('$request.path.pageId <= 3')).not.toThrow(); + expect(() => lintExpression('$request.query.pageSize != 2')).not.toThrow(); + }); + + it('should throw an error if the expression is not valid', () => { + expect(() => lintExpression('$request.body.name != Jim')).toThrowError( + 'Runtime expression is not valid: $request.body.name != Jim' + ); + expect(() => + lintExpression("$request == 'Mermaid Treasure Identification and Analysis'") + ).toThrowError( + "Runtime expression is not valid: $request == 'Mermaid Treasure Identification and Analysis'" + ); + }); + }); + + describe('$response', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression('$response.body#/name != "Jim"')).not.toThrow(); + expect(() => lintExpression('$response.body#/page/hasPrevPage == false')).not.toThrow(); + expect(() => lintExpression('$response.body#/page/hasPrevPage != null')).not.toThrow(); + expect(() => lintExpression('$response.body#/name')).not.toThrow(); + expect(() => lintExpression('$response.body#/items == []')).not.toThrow(); + expect(() => lintExpression('$response.body#/name/bob/sam')).not.toThrow(); + expect(() => lintExpression('{$response.body#/name/bob/sam}')).not.toThrow(); + expect(() => lintExpression('{$response.header.accept}')).not.toThrow(); + expect(() => lintExpression("$response.header.accept == 'application/json'")).not.toThrow(); + expect(() => lintExpression('$response.path.pageId >= 3')).not.toThrow(); + expect(() => lintExpression('$response.query.pageSize > 2')).not.toThrow(); + expect(() => lintExpression('$response.body')).not.toThrow(); + expect(() => lintExpression('$response.body == "some string"')).not.toThrow(); + }); + + it('should throw an error if the expression is not valid', () => { + expect(() => lintExpression('$response.body.name == Jim')).toThrowError( + 'Runtime expression is not valid: $response.body.name == Jim' + ); + expect(() => + lintExpression("$response.test == 'Mermaid Treasure Identification and Analysis'") + ).toThrowError( + "Runtime expression is not valid: $response.test == 'Mermaid Treasure Identification and Analysis'" + ); + }); + }); + + describe('$inputs', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression('$inputs.events.some-event.id == 42')).not.toThrow(); + }); + + it('should throw an error if the expression is not valid', () => { + expect(() => + lintExpression("$inputs.body#/name == 'Mermaid Treasure Identification and Analysis'") + ).toThrowError( + "Runtime expression is not valid: $inputs.body#/name == 'Mermaid Treasure Identification and Analysis'" + ); + }); + }); + + describe('$outputs', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression('$outputs.events.some-event.id == 42')).not.toThrow(); + }); + + it('should not throw an error if the expression is not valid', () => { + expect(() => + lintExpression("$outputs#/name == 'Mermaid Treasure Identification and Analysis'") + ).toThrowError( + "Runtime expression is not valid: $outputs#/name == 'Mermaid Treasure Identification and Analysis'" + ); + }); + + it('should not throw an error in situations where the output named property return payloads, references may be made to portions of the response body', () => { + expect(() => + lintExpression( + "$outputs.mappedResponse#/name == 'Mermaid Treasure Identification and Analysis'" + ) + ).not.toThrow(); + }); + }); + + describe('$workflows', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression('{$workflows.foo.inputs.username == "Johny"}')).not.toThrow(); + }); + + it('should not throw an error in situations where the workflow output named property return payloads, references may be made to portions of the response body', () => { + expect(() => + lintExpression( + "$workflows.foo.outputs.mappedResponse#/name == 'Mermaid Treasure Identification and Analysis'" + ) + ).not.toThrow(); + }); + + it('should throw an error if the expression is not valid', () => { + expect(() => + lintExpression( + "$workflow.foo.inputs.username == 'Mermaid Treasure Identification and Analysis'" + ) + ).toThrowError( + "Runtime expression is not valid: $workflow.foo.inputs.username == 'Mermaid Treasure Identification and Analysis'" + ); + }); + }); + + describe('$steps', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression('{$steps.someStep.pets == "Johny"}')).not.toThrow(); + expect(() => lintExpression('$steps.someStep.pets')).not.toThrow(); + }); + + it('should not throw an error in situations where the step output named property return payloads, references may be made to portions of the response body', () => { + expect(() => lintExpression("$steps.someStepId.outputs.pets#/0/id == 'uuid'")).not.toThrow(); + }); + + it('should throw an error if the expression is not valid', () => { + expect(() => lintExpression("$step.foo.outputs.username == 'Bob'")).toThrowError( + "Runtime expression is not valid: $step.foo.outputs.username == 'Bob'" + ); + expect(() => lintExpression('$step.foo.outputs#/username/test')).toThrowError( + 'Runtime expression is not valid: $step.foo.outputs#/username/test' + ); + }); + }); + + describe('$sourceDescriptions', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression('{$sourceDescriptions.petstoreDescription.url}')).not.toThrow(); + }); + + it('should throw an error if the expression is not valid', () => { + expect(() => + lintExpression("{$sourceDescriptions.petstoreDescription.url#/paths} == 'Bob'") + ).toThrowError( + "Runtime expression is not valid: {$sourceDescriptions.petstoreDescription.url#/paths} == 'Bob'" + ); + }); + }); + + describe('$components', () => { + it('should not throw an error and return the parsed expression tokens', () => { + expect(() => lintExpression('$components.parameters.foo')).not.toThrow(); + }); + + it('should throw an error if the expression is not valid', () => { + expect(() => lintExpression('test $components.parameters.foo')).toThrowError( + 'Runtime expression is not valid: test $components.parameters.foo' + ); + }); + }); + + describe('$faker', () => { + it('should throw an error if the expression is not valid', () => { + expect(() => lintExpression('$faker.number.integer({ min: 1, max: 10 })')).toThrowError( + 'Runtime expression is not valid: $faker.number.integer({ min: 1, max: 10 })' + ); + expect(() => lintExpression('$faker.sentence.integer({ min: 1, max: 10 })')).toThrowError( + 'Runtime expression is not valid: $faker.sentence.integer({ min: 1, max: 10 })' + ); + }); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/runtime-expressions/replace-json-pointers.test.ts b/packages/respect-core/src/modules/__tests__/runtime-expressions/replace-json-pointers.test.ts new file mode 100644 index 0000000000..82df62884b --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/runtime-expressions/replace-json-pointers.test.ts @@ -0,0 +1,172 @@ +import { replaceJSONPointers } from '../../runtime-expressions/replace-json-pointers'; + +describe('replaceJSONPointers', () => { + it('should replace valid $response.body pointers with the correct value from context', () => { + const context = { + $response: { + body: { + user: { + name: 'John Doe', + age: 30, + }, + }, + }, + }; + + const expression = 'Hello, $response.body#/user/name!'; + const result = replaceJSONPointers(expression, context); + + expect(result).toBe('Hello, "John Doe"!'); + }); + + it('should replace valid $request.body pointers with the correct value from context', () => { + const context = { + $request: { + body: { + order: { + id: '12345', + amount: 100, + }, + }, + }, + }; + + const expression = 'Order ID: $request.body#/order/id and Amount: $request.body#/order/amount'; + const result = replaceJSONPointers(expression, context); + + expect(result).toBe('Order ID: "12345" and Amount: 100'); + }); + + it('should return the original expression if pointer is invalid', () => { + const context = { + $response: { + body: { + user: { + name: 'John Doe', + }, + }, + }, + }; + + const expression = 'Hello, $response.body#/invalid/path!'; + const result = replaceJSONPointers(expression, context); + + // The expression should remain unchanged if the pointer is invalid + expect(result).toBe('Hello, $response.body#/invalid/path!'); + }); + + it('should return the original expression if the context is missing', () => { + const context = {}; // No $response or $request in context + + const expression = 'Hello, $response.body#/user/name!'; + const result = replaceJSONPointers(expression, context); + + // The expression should remain unchanged if context is missing + expect(result).toBe('Hello, $response.body#/user/name!'); + }); + + it('should handle multiple valid pointers in a single expression', () => { + const context = { + $response: { + body: { + user: { + name: 'John Doe', + }, + order: { + id: '12345', + }, + }, + }, + }; + + const expression = 'User: $response.body#/user/name, Order ID: $response.body#/order/id'; + const result = replaceJSONPointers(expression, context); + + expect(result).toBe('User: "John Doe", Order ID: "12345"'); + }); + + it('should replace valid $response.body pointers with the correct value from context', () => { + const context = { + $response: { + body: { + notification: { + type: 'info', + message: 'Service will be unavailable tonight.', + }, + }, + }, + }; + + const expression = 'Notification: $response.body#/notification/message'; + const result = replaceJSONPointers(expression, context); + + expect(result).toBe('Notification: "Service will be unavailable tonight."'); + }); + + it('should return original expression if pointer does not exist in body', () => { + const context = { + $response: { + body: { + data: { + value: 42, + }, + }, + }, + }; + + const expression = 'Result: $response.body#/nonexistent/path'; + const result = replaceJSONPointers(expression, context); + + expect(result).toBe('Result: $response.body#/nonexistent/path'); + }); + + it('should return number values without quotes', () => { + const context = { + $response: { + body: { + order: { + amount: 100, + }, + }, + }, + }; + + const expression = 'Amount: $response.body#/order/amount'; + const result = replaceJSONPointers(expression, context); + + expect(result).toBe('Amount: 100'); // No quotes for numbers + }); + + it('should return the original match if the value is undefined', () => { + const context = { + $response: { + body: { + data: { + value: undefined, // Pointer exists but is undefined + }, + }, + }, + }; + + const expression = 'Data: $response.body#/data/value'; + const result = replaceJSONPointers(expression, context); + + // Since the value is undefined, it should return the original match + expect(result).toBe('Data: $response.body#/data/value'); + }); + + it('should correctly replace empty array values', () => { + const context = { + $response: { + body: { + items: [], + }, + }, + }; + + const expression = '$response.body#/items == []'; + const result = replaceJSONPointers(expression, context); + + expect(result).toBe('[] == []'); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/test-config-generator/cleanup-test-description.test.ts b/packages/respect-core/src/modules/__tests__/test-config-generator/cleanup-test-description.test.ts new file mode 100644 index 0000000000..66482cd98f --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/test-config-generator/cleanup-test-description.test.ts @@ -0,0 +1,56 @@ +import type { TestDescription } from '../../../types'; + +import { cleanupTestDescription } from '../../test-config-generator'; + +describe('cleanupTestDescription', () => { + it('should cleanup sensitive information from a test config', () => { + const testDescription = { + workflows: [ + { + workflowId: 'some-workflow-id', + parameters: [ + { + 'some-default-key': 'some-default-value', + }, + ], + steps: [ + { + stepId: 'some-step-id', + parameters: [ + { + name: 'some-parameter-name', + value: 'some-parameter-value', + }, + ], + successCriteria: {}, + }, + ], + }, + ], + arazzo: 'some-workflow-spec', + sourceDescriptions: 'some-source-descriptions', + 'x-serverUrl': 'some-server-url', + } as unknown as TestDescription; + + expect(cleanupTestDescription(testDescription)).toEqual({ + workflows: [ + { + workflowId: 'some-workflow-id', + steps: [ + { + stepId: 'some-step-id', + parameters: [ + { + name: 'some-parameter-name', + }, + ], + successCriteria: {}, + }, + ], + }, + ], + arazzo: 'some-workflow-spec', + sourceDescriptions: 'some-source-descriptions', + }); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/test-config-generator/generate-example-value.test.ts b/packages/respect-core/src/modules/__tests__/test-config-generator/generate-example-value.test.ts new file mode 100644 index 0000000000..d29c708ef3 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/test-config-generator/generate-example-value.test.ts @@ -0,0 +1,44 @@ +import type { Parameter } from '../../../types'; + +import { generateExampleValue } from '../../test-config-generator'; + +describe('generateExampleValue', () => { + it('should generate example value from parameter example', () => { + const parameter = { + in: 'header', + name: 'some-parameter-in-header', + value: 'test-value', + example: 'some-example', + } as Parameter; + + expect(generateExampleValue(parameter)).toEqual('some-example'); + }); + + it('should generate example value from parameter examples', () => { + const parameter = { + in: 'query', + name: 'some-parameter-in-header', + value: 'test-value', + examples: { + 'some-example-key': { + value: 'some-example-value', + }, + }, + } as Parameter; + + expect(generateExampleValue(parameter)).toEqual('some-example-value'); + }); + + it('should generate example value from parameter schema', () => { + const parameter = { + in: 'path', + name: 'some-parameter-in-header', + value: 'test-value', + schema: { + type: 'string', + }, + } as Parameter; + + expect(generateExampleValue(parameter)).toEqual('string'); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/test-config-generator/generate-test-config.test.ts b/packages/respect-core/src/modules/__tests__/test-config-generator/generate-test-config.test.ts new file mode 100644 index 0000000000..5a7aa9cf99 --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/test-config-generator/generate-test-config.test.ts @@ -0,0 +1,220 @@ +import { generateTestConfig } from '../../test-config-generator'; +import { + bundleOpenApi, + getOperationFromDescription, + getRequestDataFromOpenApi, +} from '../../description-parser'; + +jest.mock('../../description-parser', () => ({ + bundleOpenApi: jest.fn(), + getOperationFromDescription: jest.fn(), + getRequestDataFromOpenApi: jest.fn(), +})); + +const BUNDLED_DESCRIPTION_MOCK = { + paths: { + '/pet': { + get: { + operationId: 'getPet', + responses: { + 200: { + description: 'OK', + }, + }, + }, + }, + '/fact': { + get: { + operationId: 'getFact', + responses: {}, + }, + }, + }, + servers: [ + { + url: 'https://petstore.swagger.io/v1', + }, + ], + info: { + title: 'Swagger Petstore', + version: '1.0.0', + }, +}; + +describe('generateTestConfig', () => { + it('should generate test config', async () => { + (bundleOpenApi as jest.Mock).mockReturnValue(BUNDLED_DESCRIPTION_MOCK); + expect( + await generateTestConfig({ + descriptionPath: 'description.yaml', + extended: false, + }) + ).toEqual({ + arazzo: '1.0.1', + info: { + title: 'Swagger Petstore', + version: '1.0.0', + }, + sourceDescriptions: [ + { + name: 'description', + type: 'openapi', + url: 'description.yaml', + }, + ], + workflows: [ + { + workflowId: 'get-pet-workflow', + steps: [ + { + operationId: '$sourceDescriptions.description.getPet', + stepId: 'get-pet-step', + }, + ], + }, + { + workflowId: 'get-fact-workflow', + steps: [ + { + operationId: '$sourceDescriptions.description.getFact', + stepId: 'get-fact-step', + }, + ], + }, + ], + }); + }); + + it('should generate test config when output file is provided', async () => { + (bundleOpenApi as jest.Mock).mockReturnValue(BUNDLED_DESCRIPTION_MOCK); + expect( + await generateTestConfig({ + descriptionPath: 'description.yaml', + outputFile: '../final-test-location/output.yaml', + extended: false, + }) + ).toEqual({ + arazzo: '1.0.1', + info: { + title: 'Swagger Petstore', + version: '1.0.0', + }, + sourceDescriptions: [ + { + name: 'description', + type: 'openapi', + url: '../redocly-cli/description.yaml', + }, + ], + workflows: [ + { + workflowId: 'get-pet-workflow', + steps: [ + { + operationId: '$sourceDescriptions.description.getPet', + stepId: 'get-pet-step', + }, + ], + }, + { + workflowId: 'get-fact-workflow', + steps: [ + { + operationId: '$sourceDescriptions.description.getFact', + stepId: 'get-fact-step', + }, + ], + }, + ], + }); + }); + + it('should generate test config with extended', async () => { + (bundleOpenApi as jest.Mock).mockReturnValue(BUNDLED_DESCRIPTION_MOCK); + (getOperationFromDescription as jest.Mock).mockReturnValue({ + responses: { + 200: { + description: 'OK', + }, + }, + }); + (getRequestDataFromOpenApi as jest.Mock).mockReturnValue({ + parameters: [ + { + in: 'query', + name: 'limit', + schema: { + type: 'integer', + format: 'int32', + }, + }, + ], + }); + + expect( + await generateTestConfig({ + descriptionPath: 'description.yaml', + extended: true, + }) + ).toEqual({ + arazzo: '1.0.1', + info: { + title: 'Swagger Petstore', + version: '1.0.0', + }, + sourceDescriptions: [ + { + name: 'description', + type: 'openapi', + url: 'description.yaml', + }, + ], + workflows: [ + { + workflowId: 'get-pet-workflow', + steps: [ + { + operationId: '$sourceDescriptions.description.getPet', + stepId: 'get-pet-step', + successCriteria: [{ condition: '$statusCode == 200' }], + }, + ], + }, + { + workflowId: 'get-fact-workflow', + steps: [ + { + operationId: '$sourceDescriptions.description.getFact', + stepId: 'get-fact-step', + }, + ], + }, + ], + }); + }); + + it('should generate extended test config with not existing description', async () => { + (bundleOpenApi as jest.Mock).mockReturnValue(undefined); + expect( + await generateTestConfig({ + descriptionPath: 'description.yaml', + extended: false, + }) + ).toEqual({ + arazzo: '1.0.1', + info: { + title: '[REPLACE WITH API title]', + version: '[REPLACE WITH API version]', + }, + serverUrl: undefined, + sourceDescriptions: [ + { + name: 'description', + type: 'openapi', + url: 'description.yaml', + }, + ], + workflows: [], + }); + }); +}); diff --git a/packages/respect-core/src/modules/__tests__/test-config-generator/generate-test-data-from-json-schema.test.ts b/packages/respect-core/src/modules/__tests__/test-config-generator/generate-test-data-from-json-schema.test.ts new file mode 100644 index 0000000000..468fbfa85f --- /dev/null +++ b/packages/respect-core/src/modules/__tests__/test-config-generator/generate-test-data-from-json-schema.test.ts @@ -0,0 +1,72 @@ +import * as Sampler from 'openapi-sampler'; + +import { DefaultLogger } from '../../../utils/logger/logger'; +import { generateTestDataFromJsonSchema } from '../../test-config-generator'; + +const logger = DefaultLogger.getInstance(); + +jest.mock('openapi-sampler'); + +describe('generateTestDataFromJsonSchema', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + it('should generate test data from JSON Schema', () => { + (Sampler as jest.Mocked).sample.mockReturnValue({ name: 'string' }); + + expect( + generateTestDataFromJsonSchema({ + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + }) + ).toEqual({ + name: 'string', + }); + }); + + it('should return undefined if schema is not provided', () => { + expect(generateTestDataFromJsonSchema(undefined)).toBeUndefined(); + }); + + it('should return null if schema is not valid', () => { + (Sampler as jest.Mocked).sample.mockReturnValue(null); + expect( + generateTestDataFromJsonSchema({ + type: 'unknown', + properties: { + name: { + type: 'string', + }, + }, + }) + ).toBeNull(); + }); + + it('should log error if schema is not valid', () => { + const mockLogger = jest.spyOn(logger, 'error').mockImplementation(); + + (Sampler as jest.Mocked).sample.mockImplementation(() => { + throw new Error('Mocked error from openapi-sampler'); + }); + + expect( + generateTestDataFromJsonSchema({ + type: 'unknown', + properties: { + name: { + type: 'string', + }, + }, + }) + ).toBeUndefined(); + expect(mockLogger).toHaveBeenCalledWith( + expect.stringContaining('Mocked error from openapi-sampler') + ); + + mockLogger.mockRestore(); + }); +}); diff --git a/packages/respect-core/src/modules/checks/checks.ts b/packages/respect-core/src/modules/checks/checks.ts new file mode 100644 index 0000000000..b78ec40777 --- /dev/null +++ b/packages/respect-core/src/modules/checks/checks.ts @@ -0,0 +1,8 @@ +export const CHECKS = { + SUCCESS_CRITERIA_CHECK: 'success criteria check', + STATUS_CODE_CHECK: 'status code check', + SCHEMA_CHECK: 'schema check', + CONTENT_TYPE_CHECK: 'content-type check', + UNEXPECTED_ERROR: 'unexpected error', + NETWORK_ERROR: 'failed network request', +}; diff --git a/packages/respect-core/src/modules/checks/index.ts b/packages/respect-core/src/modules/checks/index.ts new file mode 100644 index 0000000000..c04baab325 --- /dev/null +++ b/packages/respect-core/src/modules/checks/index.ts @@ -0,0 +1,2 @@ +export * from './checks'; +export * from './severity'; diff --git a/packages/respect-core/src/modules/checks/severity.ts b/packages/respect-core/src/modules/checks/severity.ts new file mode 100644 index 0000000000..0adc8bfe03 --- /dev/null +++ b/packages/respect-core/src/modules/checks/severity.ts @@ -0,0 +1,38 @@ +import { formatCliInputs } from '../flow-runner'; + +import type { CHECKS } from './checks'; +import type { RuleSeverity } from '@redocly/openapi-core/lib/config/types'; + +export const DEFAULT_SEVERITY_CONFIGURATION: { + [key in keyof typeof CHECKS]: RuleSeverity; +} = { + SUCCESS_CRITERIA_CHECK: 'error', + STATUS_CODE_CHECK: 'error', + SCHEMA_CHECK: 'error', + CONTENT_TYPE_CHECK: 'error', + UNEXPECTED_ERROR: 'error', + NETWORK_ERROR: 'error', +}; + +export function resolveSeverityConfiguration(severityArgument: string | string[] | undefined): { + [key in keyof typeof CHECKS]: RuleSeverity; +} { + if (!severityArgument) { + return DEFAULT_SEVERITY_CONFIGURATION; + } + + const severityConfiguration = formatCliInputs(severityArgument); + + if (Object.keys(severityConfiguration).length === 0) { + throw new Error( + `Failed to parse severity configuration, please check the format ${severityArgument}` + ); + } + + return { + ...DEFAULT_SEVERITY_CONFIGURATION, + ...severityConfiguration, + UNEXPECTED_ERROR: 'error', + NETWORK_ERROR: 'error', + }; +} diff --git a/packages/respect-core/src/modules/cli-output/calculate-tests-passed.ts b/packages/respect-core/src/modules/cli-output/calculate-tests-passed.ts new file mode 100644 index 0000000000..d795e02f97 --- /dev/null +++ b/packages/respect-core/src/modules/cli-output/calculate-tests-passed.ts @@ -0,0 +1,68 @@ +// import type { RuleSeverity } from '@redocly/openapi-core/lib/config/types'; +import type { CalculatedResults, Workflow } from '../../types'; + +export function calculateTotals(workflows: Workflow[]): CalculatedResults { + const totalWorkflows = workflows.length; + let failedChecks = 0; + let totalChecks = 0; + let totalSteps = 0; + let totalWarnings = 0; + let totalSkipped = 0; + const failedWorkflows = new Set(); + const workflowsWithSkippedStepsChecks = new Set(); + const workflowsWithWarningsStepsChecks = new Set(); + const failedSteps = new Set(); + const skippedSteps = new Set(); + const warningsSteps = new Set(); + + for (const workflow of workflows) { + for (const step of workflow.steps) { + totalSteps++; + for (const check of step.checks) { + totalChecks++; + if (!check.pass) { + switch (check.severity) { + case 'warn': + totalWarnings++; + workflowsWithWarningsStepsChecks.add(workflow.workflowId); + warningsSteps.add(workflow.workflowId + ':' + step.stepId); + break; + case 'off': + totalSkipped++; + workflowsWithSkippedStepsChecks.add(workflow.workflowId); + skippedSteps.add(workflow.workflowId + ':' + step.stepId); + break; + default: + failedChecks++; + failedWorkflows.add(workflow.workflowId); + failedSteps.add(workflow.workflowId + ':' + step.stepId); + } + } + } + } + } + + return { + workflows: { + passed: totalWorkflows - failedWorkflows.size, + failed: failedWorkflows.size, + warnings: workflowsWithWarningsStepsChecks.size, + skipped: workflowsWithSkippedStepsChecks.size, + total: totalWorkflows, + }, + steps: { + passed: totalSteps - failedSteps.size, + failed: failedSteps.size, + warnings: warningsSteps.size, + skipped: skippedSteps.size, + total: totalSteps, + }, + checks: { + passed: totalChecks - failedChecks, + failed: failedChecks, + warnings: totalWarnings, + skipped: totalSkipped, + total: totalChecks, + }, + }; +} diff --git a/packages/respect-core/src/modules/cli-output/display-checks.ts b/packages/respect-core/src/modules/cli-output/display-checks.ts new file mode 100644 index 0000000000..69fb897c0f --- /dev/null +++ b/packages/respect-core/src/modules/cli-output/display-checks.ts @@ -0,0 +1,119 @@ +import { blue, yellow, green, gray, red } from 'colorette'; +import { outdent } from 'outdent'; +import { combineUrl } from '../../utils/url'; +import { isJSON } from '../../utils/is-json'; +import { indent, RESET_ESCAPE_CODE } from '../../utils/cli-outputs'; +import { DefaultLogger } from '../../utils/logger/logger'; + +import type { RuleSeverity } from '@redocly/openapi-core/lib/config/types'; +import type { Check, VerboseLog } from '../../types'; + +const logger = DefaultLogger.getInstance(); + +export function displayChecks( + testNameToDisplay: string, + checks: Check[], + verboseLogs?: VerboseLog, + verboseResponseLogs?: VerboseLog +) { + const allChecksPassed = checks.every(({ pass }) => pass); + logger.log(` ${allChecksPassed ? green('✓') : red('✗')} ${blue(testNameToDisplay)}`); + + if (verboseLogs) { + logger.log(`${RESET_ESCAPE_CODE}\n` + (displayVerboseLogs(verboseLogs) || '')); + logger.printNewLine(); + } + if (verboseResponseLogs) { + logger.log( + `${RESET_ESCAPE_CODE}\n` + (displayVerboseLogs(verboseResponseLogs, 'response') || '') + ); + logger.printNewLine(); + } + logger.printNewLine(); + + for (const check of checks) { + const { name: checkName, pass, severity } = check; + const passTestMessage = (checkName: string) => + `${green('✓')} ${gray(checkName.toLowerCase())}${ + check?.additionalMessage ? ` (${check.additionalMessage})` : '' + }`; + + const failTestMessage = (checkName: string, severity?: RuleSeverity) => + `${severity === 'warn' ? yellow('⚠') : red('✗')} ${gray(checkName.toLowerCase())}${ + check?.additionalMessage ? ' (' + check.additionalMessage + ')' : '' + }`; + + logger.log( + `${ + pass + ? indent(passTestMessage(checkName), 4) + : indent(failTestMessage(checkName, severity), 4) + }\n` + ); + } +} + +function displayVerboseLogs(logs: VerboseLog, type: 'request' | 'response' = 'request'): string { + const { path, host, headerParams, body, statusCode } = logs; + const responseTime = process.env.NODE_ENV === 'test' ? '' : logs.responseTime; + + const urlString = indent(`Request URL: ${blue(combineUrl(host, path))}`, 4); + const requestHeadersString = indent(`Request Headers:`, 4); + const responseHeadersString = indent(`Response Headers:`, 4); + const requestBodyString = indent(`Request Body:`, 4); + const responseBodyString = indent(`Response Body:`, 4); + const headersString = generateHeaderString(headerParams || {}); + const formattedBody = body + ? isJSON(body) + ? JSON.stringify(JSON.parse(body), null, 2) + : body + : undefined; + + const indentedBody = formattedBody + ? formattedBody + .split('\n') + .map((line: string) => blue(indent(`${line}`, 6))) + .join('\n') + : undefined; + + const bodyString = indentedBody; + + const requestOutput = [ + logger.printNewLine(), + gray(urlString), + headersString && gray(requestHeadersString), + headersString && gray(headersString), + body && gray(requestBodyString), + body && bodyString, + ] + .filter(Boolean) + .join(`${RESET_ESCAPE_CODE}\n`); + + const responseOutput = [ + gray(indent('Response status code: ' + blue(statusCode as number), 4)), + gray(indent('Response time: ' + blue(responseTime as number), 4) + ' ms'), + headersString && gray(responseHeadersString), + headersString && gray(headersString), + body && gray(responseBodyString), + body && bodyString, + ] + .filter(Boolean) + .join(`${RESET_ESCAPE_CODE}\n`); + + return type === 'request' ? outdent`${requestOutput}` : outdent`${responseOutput}`; +} + +function generateHeaderString(headerParams: Record): string { + let compiledString = ''; + const entries = Object.entries(headerParams); + + entries.forEach(([key, value], index) => { + compiledString = compiledString + indent(`${yellow(key)}: ${blue(value)}`, 6); + + if (index < entries.length - 1) { + compiledString += `${RESET_ESCAPE_CODE}\n`; + } + }); + + return compiledString; +} diff --git a/packages/respect-core/src/modules/cli-output/display-errors.ts b/packages/respect-core/src/modules/cli-output/display-errors.ts new file mode 100644 index 0000000000..31d36f2bc9 --- /dev/null +++ b/packages/respect-core/src/modules/cli-output/display-errors.ts @@ -0,0 +1,56 @@ +import { red, gray, underline, blue, yellow } from 'colorette'; +import { CHECKS } from '../checks'; +import { indent, removeExtraIndentation, RESET_ESCAPE_CODE } from '../../utils/cli-outputs'; +import { DefaultLogger } from '../../utils/logger/logger'; + +import type { Workflow } from '../../types'; + +const logger = DefaultLogger.getInstance(); + +export function displayErrors(workflows: Workflow[]) { + logger.log(`${RESET_ESCAPE_CODE}\n${indent(red('Failed tests info:'), 2)}\n`); + + for (const workflow of workflows) { + const hasProblems = workflow.steps.some((step) => step.checks.some((check) => !check.pass)); + + if (!hasProblems) continue; + + logger.log( + `${RESET_ESCAPE_CODE}\n${indent(gray('Workflow name:'), 2)} ${underline( + workflow.workflowId + )}${RESET_ESCAPE_CODE}\n` + ); + + for (const step of workflow.steps) { + const failedStepChecks = step.checks.filter((check) => !check.pass); + + if (!failedStepChecks.length) continue; + + logger.printNewLine(); + logger.log( + indent(`${blue('stepId - ')}`, 4) + + (step?.stepId ? red(step.stepId) : red(step?.operationId || step?.operationPath || '')) + ); + + for (const failedCheckIndex in failedStepChecks) { + const { name, message, severity } = failedStepChecks[failedCheckIndex]; + const showErrorMessage = name !== CHECKS.UNEXPECTED_ERROR; + const messageToDisplay = showErrorMessage + ? indent(`${removeExtraIndentation(message)}${RESET_ESCAPE_CODE}\n`, 6) + : indent(`Reason: ${message}`, 4); + + logger.printNewLine(); + + if (severity === 'error') { + logger.log(indent(`${red('✗')} ${gray(name.toLowerCase())}`, 4)); + } else if (severity === 'off') { + logger.log(indent(`${gray('○')} ${gray(name.toLowerCase())} ${gray('(skipped)')}`, 4)); + } else { + logger.log(indent(`${yellow('⚠')} ${gray(name.toLowerCase())}`, 4)); + } + logger.printNewLine(); + logger.log(`${messageToDisplay}`); + } + } + } +} diff --git a/packages/respect-core/src/modules/cli-output/display-files-summary-table.ts b/packages/respect-core/src/modules/cli-output/display-files-summary-table.ts new file mode 100644 index 0000000000..b1970205a1 --- /dev/null +++ b/packages/respect-core/src/modules/cli-output/display-files-summary-table.ts @@ -0,0 +1,105 @@ +import { green, red, gray, yellow } from 'colorette'; +import * as path from 'path'; +import { calculateTotals } from './calculate-tests-passed'; +import { RESET_ESCAPE_CODE } from '../../utils/cli-outputs'; +import { DefaultLogger } from '../../utils/logger/logger'; + +import type { Workflow } from '../../types'; + +const logger = DefaultLogger.getInstance(); + +export function displayFilesSummaryTable( + filesResult: { + file: string; + hasProblems: boolean; + workflows: Workflow[]; + argv?: { workflow?: string[]; skip?: string[] }; + }[] +) { + const DEFAULT_FILENAME_PADDING = 40; + const maxFilenameLength = Math.max( + ...filesResult.map(({ file }) => path.basename(file).length + DEFAULT_FILENAME_PADDING) + ); + + const columns = [ + { name: 'Filename', width: maxFilenameLength }, + { name: 'Workflows', width: 10 }, + { name: 'Passed', width: 7 }, + { name: 'Failed', width: 7 }, + { name: 'Warnings', width: 8 }, + { name: 'Skipped', width: 7 }, + ]; + + let output = ''; + + // Top line + output += `${gray(`┌${columns.map((col) => '─'.repeat(col.width + 2)).join('┬')}┐`)}\n`; + + // Header + output += `${gray(`│${columns.map((col) => ` ${col.name.padEnd(col.width)} `).join('│')}│`)}\n`; + + // Separator + output += `${gray(`├${columns.map((col) => '─'.repeat(col.width + 2)).join('┼')}┤`)}\n`; + + // Data rows + filesResult.forEach(({ file, workflows, argv }) => { + const fileName = path.basename(file); + const workflowArgv = argv?.workflow || []; + const skippedWorkflowArgv = argv?.skip || []; + + let executedWorkflows = + workflowArgv && workflowArgv.length + ? workflows.filter(({ workflowId }) => workflowArgv.includes(workflowId)) + : workflows; + executedWorkflows = + skippedWorkflowArgv && skippedWorkflowArgv.length + ? executedWorkflows.filter(({ workflowId }) => !skippedWorkflowArgv.includes(workflowId)) + : executedWorkflows; + + const { workflows: testWorkflows } = calculateTotals(executedWorkflows); + const total = gray(testWorkflows.total.toString().padEnd(11)); + const passed = green(testWorkflows.passed.toString().padEnd(8)); + const failed = + testWorkflows.failed > 0 + ? red(testWorkflows.failed.toString().padEnd(8)) + : gray('-'.padEnd(8)); + + const warnings = + testWorkflows.warnings > 0 + ? yellow(testWorkflows.warnings.toString().padEnd(9)) + : gray('-'.padEnd(9)); + const skipped = + testWorkflows.skipped > 0 + ? gray(testWorkflows.skipped.toString().padEnd(8)) + : gray('-'.padEnd(8)); + + // First pad the content, then add colors + const statusSymbol = testWorkflows.failed > 0 ? 'x' : '✓'; + const paddedContent = `${statusSymbol} ${fileName}`.padEnd(maxFilenameLength + 1); + const fileNameWithStatus = testWorkflows.failed > 0 ? red(paddedContent) : green(paddedContent); + + output += + gray('│') + + ` ${fileNameWithStatus}` + + gray('│') + + ` ${total}` + + gray('│') + + ` ${passed}` + + gray('│') + + ` ${failed}` + + gray('│') + + ` ${warnings}` + + gray('│') + + ` ${skipped}` + + gray('│') + + '\n'; + }); + + // Bottom line + output += `${gray( + `└${columns.map((col) => '─'.repeat(col.width + 2)).join('┴')}┘` + )}${RESET_ESCAPE_CODE}\n`; + + // Add a single reset at the very end + logger.log(output); +} diff --git a/packages/respect-core/src/modules/cli-output/display-summary.ts b/packages/respect-core/src/modules/cli-output/display-summary.ts new file mode 100644 index 0000000000..c65450df5a --- /dev/null +++ b/packages/respect-core/src/modules/cli-output/display-summary.ts @@ -0,0 +1,69 @@ +import { outdent } from 'outdent'; +import { yellow, inverse, bold, green, red, blue, gray } from 'colorette'; +import * as path from 'path'; +import { getExecutionTime } from '../../utils/time'; +import { calculateTotals } from './calculate-tests-passed'; +import { indent } from '../../utils/cli-outputs'; +import { resolveRunningWorkflows } from '../flow-runner'; +import { DefaultLogger } from '../../utils/logger/logger'; + +import type { ResultsOfTests, Workflow } from '../../types'; + +const logger = DefaultLogger.getInstance(); + +export function displaySummary( + startedAt: number, + workflows: Workflow[], + argv?: { workflow?: string[]; skip?: string[]; file?: string } +) { + const fileName = path.basename(argv?.file || ''); + const workflowArgv = resolveRunningWorkflows(argv?.workflow) || []; + const skippedWorkflowArgv = resolveRunningWorkflows(argv?.skip) || []; + + let executedWorkflows = + workflowArgv && workflowArgv.length + ? workflows.filter(({ workflowId }) => workflowArgv.includes(workflowId)) + : workflows; + + executedWorkflows = + skippedWorkflowArgv && skippedWorkflowArgv.length + ? executedWorkflows.filter(({ workflowId }) => !skippedWorkflowArgv.includes(workflowId)) + : executedWorkflows; + + const totals = calculateTotals(executedWorkflows); + + const executionTime = getExecutionTime(startedAt); + logger.printNewLine(); + logger.log( + outdent` + ${yellow(indent(`Summary for ${blue(fileName)}`, 2))} + ${indent('', 2)} + ${indent(formatWorkflowsTotals('Workflows:', totals.workflows), 2)} + ${indent(formatTotals('Steps:', totals.steps), 2)} + ${indent(formatTotals('Checks:', totals.checks), 2)} + ${indent(inverse(`Time: ${executionTime}`), 2)} + ` + ); + logger.printNewLine(); + logger.printNewLine(); +} + +function formatWorkflowsTotals(header: string, totals: ResultsOfTests): string { + return ( + bold(header) + + (totals.passed ? ` ${green(totals.passed + ' passed')},` : '') + + (totals.failed ? ` ${red(totals.failed + ' failed')},` : '') + + ` ${totals.total} total` + ); +} + +function formatTotals(header: string, totals: ResultsOfTests): string { + return ( + bold(header) + + (totals.passed ? ` ${green(totals.passed + ' passed')},` : '') + + (totals.failed ? ` ${red(totals.failed + ' failed')},` : '') + + (totals.warnings ? ` ${yellow(totals.warnings + ' warnings')},` : '') + + (totals.skipped ? ` ${gray(totals.skipped + ' ignored')},` : '') + + ` ${totals.total} total` + ); +} diff --git a/packages/respect-core/src/modules/cli-output/index.ts b/packages/respect-core/src/modules/cli-output/index.ts new file mode 100644 index 0000000000..5d78a7c3ad --- /dev/null +++ b/packages/respect-core/src/modules/cli-output/index.ts @@ -0,0 +1,8 @@ +export * from './display-summary'; +export * from './display-errors'; +export * from './display-checks'; +export * from './calculate-tests-passed'; +export * from './display-files-summary-table'; +export * from './verbose-logs'; +export * from './mask-secrets'; +export * from './json-logs'; diff --git a/packages/respect-core/src/modules/cli-output/json-logs.ts b/packages/respect-core/src/modules/cli-output/json-logs.ts new file mode 100644 index 0000000000..c20956b797 --- /dev/null +++ b/packages/respect-core/src/modules/cli-output/json-logs.ts @@ -0,0 +1,31 @@ +import { maskSecrets } from './mask-secrets'; + +import type { TestContext, JsonLogs } from '../../types'; + +export function composeJsonLogs(ctx: TestContext): JsonLogs { + const { secretFields } = ctx; + const jsonLogs = { ...ctx.$workflows } as JsonLogs; + + for (const workflow of ctx.workflows) { + const workflowId = workflow.workflowId; + jsonLogs[workflowId].time = workflow.time; + + for (const step of workflow.steps) { + const stepId = step.stepId; + + if (jsonLogs[workflowId] && jsonLogs[workflowId].steps[stepId]) { + jsonLogs[workflowId].steps[stepId].checks = step.checks; + + if (step.verboseLog) { + const { host, path } = step.verboseLog; + // Log resolved url + if (jsonLogs[workflowId].steps[stepId]?.request) { + jsonLogs[workflowId].steps[stepId].request.url = `${host}${path}`; + } + } + } + } + } + + return maskSecrets(jsonLogs, secretFields || new Set()); +} diff --git a/packages/respect-core/src/modules/cli-output/mask-secrets.ts b/packages/respect-core/src/modules/cli-output/mask-secrets.ts new file mode 100644 index 0000000000..c0cdb4e988 --- /dev/null +++ b/packages/respect-core/src/modules/cli-output/mask-secrets.ts @@ -0,0 +1,33 @@ +export function maskSecrets(target: { [x: string]: any } | string, secretValues: Set) { + const maskValue = (): string => '*'.repeat(8); + + if (typeof target === 'string') { + let maskedString = target; + secretValues.forEach((secret) => { + maskedString = maskedString.split(secret).join('*'.repeat(secret.length)); + }); + return maskedString; + } + + const masked = JSON.parse(JSON.stringify(target)); + const maskIfContainsSecret = (value: string): string => { + return containsSecret(value, secretValues) ? maskValue() : value; + }; + + const maskRecursive = (current: any) => { + for (const key in current) { + if (typeof current[key] === 'string') { + current[key] = maskIfContainsSecret(current[key]); + } else if (typeof current[key] === 'object' && current[key] !== null) { + maskRecursive(current[key]); + } + } + }; + maskRecursive(masked); + + return masked; +} + +export function containsSecret(value: string, secretValues: Set): boolean { + return Array.from(secretValues).some((secret) => value.includes(secret)); +} diff --git a/packages/respect-core/src/modules/cli-output/verbose-logs.ts b/packages/respect-core/src/modules/cli-output/verbose-logs.ts new file mode 100644 index 0000000000..ed9a86f016 --- /dev/null +++ b/packages/respect-core/src/modules/cli-output/verbose-logs.ts @@ -0,0 +1,35 @@ +import type { VerboseLog } from '../../types'; + +export function getVerboseLogs({ + headerParams, + path, + method, + host, + body, + statusCode, + responseTime, +}: VerboseLog): VerboseLog { + const verboseLogs: VerboseLog = { + path, + method, + host, + }; + + if (headerParams && Object.keys(headerParams).length > 0) { + verboseLogs.headerParams = headerParams; + } + + if (body && Object.keys(body).length > 0) { + verboseLogs.body = body; + } + + if (statusCode) { + verboseLogs.statusCode = statusCode; + } + + if (responseTime) { + verboseLogs.responseTime = responseTime; + } + + return verboseLogs; +} diff --git a/packages/respect-core/src/modules/config-parser/get-value-from-context.ts b/packages/respect-core/src/modules/config-parser/get-value-from-context.ts new file mode 100644 index 0000000000..88a00245a1 --- /dev/null +++ b/packages/respect-core/src/modules/config-parser/get-value-from-context.ts @@ -0,0 +1,233 @@ +import { red } from 'colorette'; +import { createContext, runInContext } from 'node:vm'; +import { DefaultLogger } from '../../utils/logger/logger'; + +import type { RuntimeExpressionContext, TestContext, Workflow } from '../../types'; + +const logger = DefaultLogger.getInstance(); + +export interface ParsedParameters { + queryParams: Record; + pathParams: Record; + headerParams: Record; +} + +const hasCurlyBraces = (input: string) => { + return /\{.*?\}/.test(input); +}; + +export function getValueFromContext(value: any, ctx: TestContext | RuntimeExpressionContext): any { + if (!value) return value; + + if (typeof value === 'object') { + for (const key in value) { + value[key] = getValueFromContext(value[key], ctx); + } + return value; + } + + if (typeof value === 'number' || typeof value === 'boolean') { + return value; + } + + if (value.toString().startsWith('$faker.')) { + return getFakeData(value.slice(1), ctx); + } + + if (hasCurlyBraces(value)) { + // multiple variables in string => {$var1} {$var2} + return replaceVariablesInString(value, ctx); + } else { + // single variable in string => $var1 + return resolveValue(value, ctx); + } +} + +export function replaceFakerVariablesInString( + input: string, + ctx: TestContext | RuntimeExpressionContext +) { + const startIndex = input.indexOf('{'); + + if (startIndex !== -1) { + const substringAfterFirstBrace = input.substring(startIndex + 1); + const fakerFunction = substringAfterFirstBrace.slice(0, -1); + const fakerValue = getFakeData(fakerFunction.slice(1), ctx); + + return fakerValue && input.replace(/{(.*)}/, fakerValue); + } else { + return input; + } +} + +function replaceVariablesInString( + input: string, + ctx: TestContext | RuntimeExpressionContext +): string { + // Regular expression to match content inside ${...} + const regex = /\{\$(\{[^{}]*\}|[^{}])*\}/g; + let result = input; + + // Replace each match with its interpolated value + result = result.replace(regex, (match, _code) => { + return interpolate(match, ctx); + }); + + return result; +} + +function interpolate(part: string, ctx: TestContext | RuntimeExpressionContext): string { + if (!part.includes('$')) return part; + + if (part.includes('$faker.')) { + return replaceFakerVariablesInString(part, ctx); + } + + const value = getFrom(ctx)(removeFigureBrackets(part)); + return value !== undefined ? value : ''; +} + +const resolveValue = (value: string | null, ctx: TestContext | RuntimeExpressionContext) => { + if (!value) return value; + + const path = value.toString(); + + if (!path.includes('$')) { + return value; + } + + // if path has $file('...') syntax, return the path but trim the $file(' and ') + if (path.startsWith('$file(') && path.endsWith(')')) { + return path.slice(7, -2); + } + + // $sourceDescriptions..workflows. + if (path.startsWith('$sourceDescriptions.') && path.includes('.workflows.')) { + const parts = path.split('.'); + + const sourceDescriptionName = parts[1]; + const workflowId = parts[3]; + + if (!sourceDescriptionName || !workflowId) { + return undefined; + } + + const sourceDescriptions = getFrom(ctx)('$sourceDescriptions'); + + if (!sourceDescriptions[sourceDescriptionName]) { + return undefined; + } + + return sourceDescriptions[sourceDescriptionName].workflows.find( + (workflow: Workflow) => workflow.workflowId === workflowId + ); + } + + if (path && path.trim().startsWith('faker.')) { + return getFakeData(path, ctx); + } + + return path + ? getFrom(ctx)(replaceSquareBrackets(path)) + : path.replace(/\$\{([^}]+)}/g, (_, path) => getFrom(ctx)(replaceSquareBrackets(path))); +}; + +function removeFigureBrackets(input: string) { + return input.replace(/\{(.*)\}/, '$1'); +} + +function replaceSquareBrackets(input: string) { + return input.replace(/\['(.*?)'\]/g, '.$1'); +} + +const getFrom = + ($: Record | any, originalPointer: string = '') => + (pointer: string): any => { + if (!originalPointer) { + originalPointer = pointer; // Store the first pointer only during the initial call + } + + if (!pointer) return $; + const [key, ...rest] = pointer.split('.'); + + if (!$) { + throw Error(`Cannot get ${red(key)} from ${red(originalPointer)}`); + } + + if ($?.[key] === undefined) { + const reason = ` Undefined ${red(key)} at ${red(originalPointer)}.`; + const additionalInfo = Object.keys($).length + ? `Did you mean to use another key? Available keys:\n${Object.keys($).join(', ')}.\n` + : ''; + const errorMessage = `${reason} ${additionalInfo}`; + + throw Error(errorMessage); + } + + return getFrom($[key], originalPointer)(rest.join('.')); + }; + +export function getFakeData(pointer: string, ctx: TestContext | RuntimeExpressionContext): any { + const segments = pointer.split('.'); + const fakerContext = { ctx: { faker: ctx.$faker } }; + + try { + createContext(fakerContext); + const escapedSegments = segments + .map((segment) => segment.trim()) + .map((segment, idx) => + // Dumb function check (if ends by ')'), if function goes first dont need to put '.', + // if goes second and so on - must be prepended by '.', like ["escaped-field"].func() + segment.endsWith(')') ? `${idx == 0 ? '' : '.'}${segment}` : `["${segment}"]` + ) + .join(''); + + return runInContext(`ctx${escapedSegments}`, fakerContext); + } catch (err: any) { + logger.error(red(err.toString())); + + return undefined; + } +} + +function modifyJSON(value: any, ctx: TestContext | RuntimeExpressionContext): any { + if (typeof value === 'string') { + if (value) return getValueFromContext(value, ctx); + } + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' || + typeof value === 'undefined' || + value === null + ) + return; + + for (const i in value as Record | unknown[]) { + if (typeof value[i] === 'string') { + if (value[i]) value[i] = getValueFromContext(value[i], ctx); + } else { + modifyJSON(value[i], ctx); + } + } +} + +export function parseJson(objectToResolve: any, ctx: TestContext | RuntimeExpressionContext): any { + return modifyJSON(objectToResolve, ctx) || objectToResolve; +} + +export function resolvePath( + path?: string, + pathParams?: Record +): string | undefined { + if (!path) return; + + const paramsWithBraces: Record = {}; + for (const param in pathParams) { + paramsWithBraces[`{${param}}`] = String(pathParams[param]); + } + return path + .split(/(\{[a-zA-Z0-9_.-]+\}+)/g) + .map((key) => (paramsWithBraces[key] ? paramsWithBraces[key] : key)) + .join(''); +} diff --git a/packages/respect-core/src/modules/config-parser/handle-request-body-replacements.ts b/packages/respect-core/src/modules/config-parser/handle-request-body-replacements.ts new file mode 100644 index 0000000000..aa8e75b4b2 --- /dev/null +++ b/packages/respect-core/src/modules/config-parser/handle-request-body-replacements.ts @@ -0,0 +1,25 @@ +import { type Replacement } from '../../types'; + +const JsonPointerLib = require('json-pointer'); + +export function handlePayloadReplacements(payload: object, replacements: Replacement[]) { + for (const replacement of replacements) { + const { target, value } = replacement; + + if (typeof target !== 'string') { + throw new Error(`Invalid JSON Pointer: ${target}`); + } + + try { + // Get the current value using JSON Pointer + const currentValue = JsonPointerLib.get(payload, target); + + if (currentValue !== undefined) { + // Replace the value using JSON Pointer + JsonPointerLib.set(payload, target, value); + } + } catch { + throw new Error(`Invalid JSON Pointer: ${target}`); + } + } +} diff --git a/packages/respect-core/src/modules/config-parser/index.ts b/packages/respect-core/src/modules/config-parser/index.ts new file mode 100644 index 0000000000..9b67e45c5a --- /dev/null +++ b/packages/respect-core/src/modules/config-parser/index.ts @@ -0,0 +1,7 @@ +export * from './get-value-from-context'; +export * from './parse-parameters'; +export * from './parse-request-body'; +export * from './handle-request-body-replacements'; +export * from './resolve-reusable-object-reference'; +export * from './resolve-reusable-component'; +export * from './map-header-params-and-cookie-to-object'; diff --git a/packages/respect-core/src/modules/config-parser/map-header-params-and-cookie-to-object.ts b/packages/respect-core/src/modules/config-parser/map-header-params-and-cookie-to-object.ts new file mode 100644 index 0000000000..8a3dfdbf0b --- /dev/null +++ b/packages/respect-core/src/modules/config-parser/map-header-params-and-cookie-to-object.ts @@ -0,0 +1,21 @@ +export function mapHeaderParamsAndCookieToObject(headerParams: { [key: string]: string }): { + [key: string]: string; +} { + const headerParamsWithMappedCookies: { [key: string]: string } = {}; + + for (const [key, value] of Object.entries(headerParams)) { + if (key.toLowerCase() === 'cookie') { + const cookies = value.split(';'); + for (const cookie of cookies) { + const [cookieKey, cookieValue] = cookie.split('='); + if (cookieKey && cookieValue) { + headerParamsWithMappedCookies[cookieKey.trim()] = cookieValue.trim(); + } + } + } else { + headerParamsWithMappedCookies[key] = value; + } + } + + return headerParamsWithMappedCookies; +} diff --git a/packages/respect-core/src/modules/config-parser/parse-parameters.ts b/packages/respect-core/src/modules/config-parser/parse-parameters.ts new file mode 100644 index 0000000000..5645a3ce12 --- /dev/null +++ b/packages/respect-core/src/modules/config-parser/parse-parameters.ts @@ -0,0 +1,58 @@ +import type { AdditionalParameterProperties } from '../../types'; + +export type ParameterWithIn = { + in: 'header' | 'query' | 'path' | 'cookie'; + name: string; + value: string | number | boolean; +} & AdditionalParameterProperties; + +export type ParameterWithoutIn = Omit; + +// The isParameterWithoutIn and isParameterWithIn type guards are used to differentiate between +// the two sides of onOffs in the parameter schema that is imported from the @redocly/openapi-core. +// export const parameter = { +// type: 'object', +// oneOf: [ +// { +// type: 'object', +// properties: { +// in: { type: 'string', enum: ['header', 'query', 'path', 'cookie'] }, +// name: { type: 'string' }, +// value: { +// oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], +// }, +// }, +// required: ['name', 'value'], +// additionalProperties: false, +// }, +// { +// type: 'object', +// properties: { +// reference: { type: 'string' }, +// value: { +// oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], +// }, +// }, +// required: ['reference'], +// additionalProperties: false, +// }, +// ], +// } as const; +export function isParameterWithoutIn(parameter: any): parameter is ParameterWithoutIn { + return ( + typeof parameter === 'object' && + parameter !== null && + 'name' in parameter && + 'value' in parameter && + !('in' in parameter) + ); +} + +export function isParameterWithIn(parameter: any): parameter is ParameterWithIn { + return ( + typeof parameter === 'object' && + parameter !== null && + 'in' in parameter && + ['header', 'query', 'path', 'cookie'].includes(parameter.in) + ); +} diff --git a/packages/respect-core/src/modules/config-parser/parse-request-body.ts b/packages/respect-core/src/modules/config-parser/parse-request-body.ts new file mode 100644 index 0000000000..55c3d53a32 --- /dev/null +++ b/packages/respect-core/src/modules/config-parser/parse-request-body.ts @@ -0,0 +1,147 @@ +import { createReadStream, constants, access } from 'node:fs'; +import * as path from 'node:path'; +import FormData = require('form-data'); +import { handlePayloadReplacements } from './handle-request-body-replacements'; +import * as querystring from 'node:querystring'; +import { type RequestBody } from '../../types'; + +const KNOWN_BINARY_CONTENT_TYPES_REGEX = + /^image\/(png|jpeg|gif|bmp|webp|svg\+xml)|application\/pdf$/; + +export function stripFileDecorator(payload: string) { + return payload.startsWith('$file(') && payload.endsWith(')') + ? payload.substring(7, payload.length - 2) + : payload; +} + +const appendFileToFormData = (formData: FormData, key: string, item: string): Promise => { + return new Promise((resolve, reject) => { + const filePath = path.resolve(__dirname, '../', stripFileDecorator(item)); + + access(filePath, constants.F_OK | constants.R_OK, (err) => { + if (err) { + const relativePath = path.relative(process.cwd(), filePath); + reject(new Error(`File ${relativePath} doesn't exist or isn't readable.`)); + } else { + formData.append(key, createReadStream(filePath)); + resolve(); + } + }); + }); +}; + +const appendObjectToFormData = ( + promises: Promise[], + formData: FormData, + payload: Record, + parentKey?: string +) => { + Object.entries(payload).forEach(([key, item]) => { + const formKey = parentKey ? `${parentKey}[${key}]` : key; + + if (typeof item === 'string' && item.startsWith('$file(') && item.endsWith(')')) { + promises.push(appendFileToFormData(formData, formKey, item)); + } else if (Array.isArray(item)) { + item.forEach((i) => { + if (typeof i === 'string' && i.startsWith('$file(') && i.endsWith(')')) { + promises.push(appendFileToFormData(formData, formKey, i)); + } else { + formData.append(formKey, i.toString()); + } + }); + } else if (typeof item === 'object' && item !== null) { + appendObjectToFormData(promises, formData, item, formKey); + } else if (typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean') { + formData.append(formKey, item.toString()); + } + }); +}; + +const getRequestBodyMultipartFormData = async ( + payload: RequestBody['payload'], + formData: FormData +) => { + if (payload && typeof payload === 'object' && !Array.isArray(payload)) { + const promises: Promise[] = []; + appendObjectToFormData(promises, formData, payload); + await Promise.all(promises); + } +}; + +const getRequestBodyOctetStream = async (payload: RequestBody['payload']) => { + if (typeof payload === 'string' && payload.startsWith('$file(') && payload.endsWith(')')) { + const filePath = path.resolve(__dirname, '../', stripFileDecorator(payload)); + + await new Promise((resolve, reject) => { + access(filePath, constants.F_OK | constants.R_OK, (err) => { + if (err) { + const relativePath = path.relative(process.cwd(), filePath); + reject(new Error(`File ${relativePath} doesn't exist or isn't readable.`)); + } else { + resolve(filePath); + } + }); + }); + return createReadStream(filePath); + } else { + return payload; + } +}; + +export async function parseRequestBody(stepRequestBody: RequestBody | undefined): Promise<{ + payload: any | undefined; + contentType: string | undefined; + encoding: string | undefined; +}> { + if (!stepRequestBody) { + return { + payload: undefined, + contentType: undefined, + encoding: undefined, + }; + } + + const { payload, encoding, contentType, replacements } = stepRequestBody; + + if (contentType === 'multipart/form-data') { + const formData = new FormData(); + + await getRequestBodyMultipartFormData(payload, formData); + + return { + payload: formData, + contentType: `multipart/form-data; boundary=${formData.getBoundary()}`, + encoding, + }; + } else if ( + contentType === 'application/octet-stream' || + (typeof contentType === 'string' && KNOWN_BINARY_CONTENT_TYPES_REGEX.test(contentType)) + ) { + return { + payload: await getRequestBodyOctetStream(payload), + contentType: 'application/octet-stream', + encoding, + }; + } + + let resolvedPayload = payload; + + if (replacements) { + // To handle string replacement properly with variables it's better to parse + // the string into an object and process the replacement. + // Also resolves query string variables before sending. + if (typeof resolvedPayload === 'string') { + resolvedPayload = querystring.parse(resolvedPayload); + } + + if (typeof resolvedPayload === 'object') { + handlePayloadReplacements(resolvedPayload, replacements); + } + } + + return { + payload: resolvedPayload, + contentType, + encoding, + }; +} diff --git a/packages/respect-core/src/modules/config-parser/resolve-reusable-component.ts b/packages/respect-core/src/modules/config-parser/resolve-reusable-component.ts new file mode 100644 index 0000000000..e4733d4265 --- /dev/null +++ b/packages/respect-core/src/modules/config-parser/resolve-reusable-component.ts @@ -0,0 +1,9 @@ +import { resolveReusableObjectReference } from './resolve-reusable-object-reference'; + +import type { OnFailureObject, OnSuccessObject, Parameter, TestContext } from '../../types'; + +export function resolveReusableComponentItem< + T extends OnSuccessObject | OnFailureObject | Parameter +>(item: T, ctx: TestContext): T { + return 'reference' in item ? (resolveReusableObjectReference(item, ctx) as T) : item; +} diff --git a/packages/respect-core/src/modules/config-parser/resolve-reusable-object-reference.ts b/packages/respect-core/src/modules/config-parser/resolve-reusable-object-reference.ts new file mode 100644 index 0000000000..494dbbdebe --- /dev/null +++ b/packages/respect-core/src/modules/config-parser/resolve-reusable-object-reference.ts @@ -0,0 +1,44 @@ +import { getValueFromContext } from './get-value-from-context'; + +import type { + ReusableObject, + TestContext, + OnSuccessObject, + OnFailureObject, + Parameter, +} from '../../types'; + +type ComponentType = + T['reference'] extends `$components.successActions${string}` + ? OnSuccessObject + : T['reference'] extends `$components.failureActions${string}` + ? OnFailureObject + : T['reference'] extends `$components.parameters${string}` + ? Parameter + : never; + +const VALID_COMPONENTS = ['parameters', 'failureActions', 'successActions']; + +export function resolveReusableObjectReference( + reusableObject: T, + ctx: TestContext +): ComponentType { + const { reference, value: valueOverride } = reusableObject; + + if (!VALID_COMPONENTS.some((component) => reference.includes(`$components.${component}`))) { + throw new Error( + 'Invalid reference: available components are $components.parameters, $components.failureActions, or $components.successActions' + ); + } + + const component = getValueFromContext(reference, ctx); + + if ('value' in component && valueOverride) { + return { + ...component, + value: valueOverride, + } as ComponentType; + } + + return component as ComponentType; +} diff --git a/packages/respect-core/src/modules/description-parser/bundle-openapi.ts b/packages/respect-core/src/modules/description-parser/bundle-openapi.ts new file mode 100644 index 0000000000..73034feee7 --- /dev/null +++ b/packages/respect-core/src/modules/description-parser/bundle-openapi.ts @@ -0,0 +1,49 @@ +import { resolve, dirname } from 'node:path'; +import { existsSync } from 'node:fs'; +import { loadConfig, bundle, bundleFromString } from '@redocly/openapi-core'; +import { type BundleResult } from '@redocly/openapi-core/lib/bundle'; +import { isURL } from '../../utils/is-url'; + +export async function bundleOpenApi(path: string = '', workflowPath: string): Promise { + const isUrl = isURL(path); + const config = await loadConfig(); + let bundleDocument: BundleResult; + + if (isUrl) { + // Download OpenAPI YAML file + const response = await fetch(path); + + if (!response.ok) { + throw new Error(`Failed to fetch OpenAPI YAML file. Status: ${response.status}`); + } + + const openApiYaml = await response.text(); + bundleDocument = await bundleFromString({ + source: openApiYaml, + config, + dereference: true, + }); + } else { + const descriptionPath = path && resolve(dirname(workflowPath), path); + + if (!existsSync(descriptionPath)) { + throw new Error(`Could not find source description file '${path}' at path '${workflowPath}'`); + } + + bundleDocument = await bundle({ + ref: descriptionPath, + config, + dereference: true, + }); + } + + if (!bundleDocument) return; + + const { + bundle: { + parsed: { paths, servers, info }, + }, + } = bundleDocument; + + return { paths, servers, info }; +} diff --git a/packages/respect-core/src/modules/description-parser/extract-first-example.ts b/packages/respect-core/src/modules/description-parser/extract-first-example.ts new file mode 100644 index 0000000000..583446f2b1 --- /dev/null +++ b/packages/respect-core/src/modules/description-parser/extract-first-example.ts @@ -0,0 +1,6 @@ +export function extractFirstExample(examples: Record | undefined) { + if (typeof examples !== 'object') return; + const firstKey = Object.keys(examples)[0]; + + return firstKey ? examples[firstKey]?.value : undefined; +} diff --git a/packages/respect-core/src/modules/description-parser/get-operation-by-id.ts b/packages/respect-core/src/modules/description-parser/get-operation-by-id.ts new file mode 100644 index 0000000000..8d5955fcc4 --- /dev/null +++ b/packages/respect-core/src/modules/description-parser/get-operation-by-id.ts @@ -0,0 +1,66 @@ +import { red } from 'colorette'; + +import type { OperationDetails } from './get-operation-from-description'; + +// TODO: create a type: ExtendedOpenAPIOperation = OpenAPIOperation & { pathParameters: Parameter[], path, ... } +export function getOperationById( + operationIdStr: string, + descriptions: any +): (OperationDetails & Record) | undefined { + let descriptionName; + let operationId; + + if (operationIdStr.includes('$sourceDescriptions.')) { + const [, sourceDescriptionName, operationIdIdentifier] = operationIdStr.split('.'); + descriptionName = sourceDescriptionName; + operationId = operationIdIdentifier; + } else if (!operationIdStr.includes('.')) { + operationId = operationIdStr; + descriptionName = Object.keys(descriptions)[0]; + } else { + [descriptionName, operationId] = operationIdStr.split('.'); + } + + const availableDescriptions = Object.keys(descriptions); + + if (!descriptions[descriptionName]) { + throw new Error( + `Unknown description name ${red(descriptionName)} at ${red( + operationIdStr + )}. Available descriptions: ${availableDescriptions.join(', ')}.` + ); + } + + const description = descriptions[descriptionName]; + const rootServers = description.servers; + + for (const [path, pathDetails] of Object.entries(descriptions[descriptionName].paths)) { + if (!pathDetails) { + return undefined; + } + + for (const [method, operationDetails] of Object.entries(pathDetails)) { + // @ indicates autogenerated operationId + if (operationId.includes('@/')) { + // TODO: replace this with construct of path + const [methodPrefix, pathSuffix] = operationId.split('@'); + if (methodPrefix === method && pathSuffix === path) { + return { ...operationDetails, path, method, descriptionName }; + } + } else { + if (operationDetails.operationId === operationId) { + return { + servers: (pathDetails as any).servers || rootServers, + ...operationDetails, + pathParameters: operationDetails.parameters || [], + path, + method, + descriptionName, + }; + } + } + } + } + + throw new Error(`Unknown operationId ${red(operationId)} at ${red(operationIdStr)}.`); +} diff --git a/packages/respect-core/src/modules/description-parser/get-operation-by-path.ts b/packages/respect-core/src/modules/description-parser/get-operation-by-path.ts new file mode 100644 index 0000000000..e8273db18e --- /dev/null +++ b/packages/respect-core/src/modules/description-parser/get-operation-by-path.ts @@ -0,0 +1,65 @@ +import { red } from 'colorette'; + +import type { OperationDetails } from './get-operation-from-description'; +import type { SourceDescription } from '../../types'; + +const JsonPointerLib = require('json-pointer'); + +export function getOperationByPath( + operationPath: string, + descriptionDetails: { + $sourceDescriptions: any; + sourceDescriptions: SourceDescription[] | undefined; + } +): (OperationDetails & Record) | undefined { + const { $sourceDescriptions, sourceDescriptions } = descriptionDetails; + const [basePath, fragmentIdentifier] = operationPath.split('#'); + + if (!sourceDescriptions) { + throw new Error(`Missing described sourceDescriptions`); + } + + const descriptionName = sourceDescriptions.find((sourceDescription) => { + if (['openapi', 'arazzo'].includes(sourceDescription.type)) { + if (basePath.includes('$sourceDescriptions.')) { + const [, sourceDescriptionName] = basePath.split('.'); + return sourceDescription.name === sourceDescriptionName; + } + return 'url' in sourceDescription && sourceDescription.url === basePath; + } + return false; + })?.name; + + if (!descriptionName) { + throw new Error( + `Unknown operationPath ${red(operationPath)}. API description ${red( + basePath + )} is not listed in 'sourceDescriptions' workflow section.` + ); + } + + const description = $sourceDescriptions[descriptionName] || {}; + const [prop, path, method] = JsonPointerLib.parse(fragmentIdentifier); + + if (prop !== 'paths') { + throw new Error( + `Invalid fragment identifier: ${fragmentIdentifier} at operationPath ${red(operationPath)}.` + ); + } + + const { servers } = description; + const operation = JsonPointerLib.get(description, fragmentIdentifier) || ({} as OperationDetails); + const pathItemObject = + JsonPointerLib.get(description, JsonPointerLib.compile(['paths', path])) || + ({} as OperationDetails); + + // FIXME: use servers from path level + return { + servers: pathItemObject.servers || servers, // use servers from path level if exists + ...operation, // operation level servers override path level or global servers + pathParameters: pathItemObject.parameters || [], + path, + method, + descriptionName, + }; +} diff --git a/packages/respect-core/src/modules/description-parser/get-operation-from-description.ts b/packages/respect-core/src/modules/description-parser/get-operation-from-description.ts new file mode 100644 index 0000000000..0d623c794c --- /dev/null +++ b/packages/respect-core/src/modules/description-parser/get-operation-from-description.ts @@ -0,0 +1,45 @@ +import { type Oas3Responses } from '@redocly/openapi-core/lib/typings/openapi'; +import { type OperationMethod, type TestContext } from '../../types'; +import { getOperationById } from './get-operation-by-id'; +import { getOperationByPath } from './get-operation-by-path'; + +export type DescriptionSource = { + operationId?: string; + operationPath?: string; +}; + +export type OperationDetails = { + method: OperationMethod; + path: string; + descriptionName: string; + servers?: Array<{ url: string }>; + responses: Oas3Responses; +}; + +export function getOperationFromDescription( + path: string, + method: string, + descriptionPaths: Record +): Record | undefined { + return descriptionPaths?.[path]?.[method] as Record; +} + +export function getOperationFromDescriptionBySource( + source: DescriptionSource, + ctx: TestContext +): (OperationDetails & Record) | undefined { + if (!source.operationId && !source.operationPath) { + return undefined; + } + + const { $sourceDescriptions, sourceDescriptions } = ctx; + const { operationId, operationPath } = source; + + if (operationId) { + return getOperationById(operationId, $sourceDescriptions); + } else if (operationPath) { + return getOperationByPath(operationPath, { $sourceDescriptions, sourceDescriptions }); + } else { + return undefined; + } +} diff --git a/packages/respect-core/src/modules/description-parser/get-request-body-schema.ts b/packages/respect-core/src/modules/description-parser/get-request-body-schema.ts new file mode 100644 index 0000000000..bdd3ea5b24 --- /dev/null +++ b/packages/respect-core/src/modules/description-parser/get-request-body-schema.ts @@ -0,0 +1,14 @@ +import type { OperationDetails } from './get-operation-from-description'; + +export function getRequestBodySchema( + contentType: string, + descriptionOperation: (OperationDetails & Record) | undefined +) { + if (!descriptionOperation) return undefined; + + const requestBody = descriptionOperation.requestBody; + const requestBodyContent = requestBody?.content; + const schema = requestBodyContent && requestBodyContent[contentType]?.schema; + + return schema || undefined; +} diff --git a/packages/respect-core/src/modules/description-parser/get-request-data-from-openapi.ts b/packages/respect-core/src/modules/description-parser/get-request-data-from-openapi.ts new file mode 100644 index 0000000000..ff1d7ab601 --- /dev/null +++ b/packages/respect-core/src/modules/description-parser/get-request-data-from-openapi.ts @@ -0,0 +1,86 @@ +import { isPlainObject } from '@redocly/openapi-core/lib/utils'; +import { generateTestDataFromJsonSchema, generateExampleValue } from '../test-config-generator'; +import { extractFirstExample } from './extract-first-example'; +import { isParameterWithIn } from '../config-parser'; + +import type { Parameter } from '../../types'; +import type { ParameterWithIn } from '../config-parser'; +import type { OperationDetails } from './get-operation-from-description'; + +export interface OpenApiRequestData { + requestBody?: Record; + contentType?: string; + parameters: ParameterWithIn[]; +} + +export function getRequestDataFromOpenApi( + operation: OperationDetails & Record +): OpenApiRequestData { + const content: Record = operation?.requestBody?.content || {}; + const [contentType, contentItem]: [string, any] = Object.entries(content)[0] || []; + + const requestBody = + contentItem?.example || + extractFirstExample(contentItem?.examples) || + generateTestDataFromJsonSchema(contentItem?.schema); + + const accept = getAcceptHeader(operation); + const parameters = getUniqueParameters([ + ...transformParameters(operation.pathParameters), + { name: 'content-type', in: 'header' as const, value: contentType }, + ...(accept ? [{ name: 'accept', in: 'header' as const, value: accept }] : []), + ...transformParameters(operation.parameters), + ]).filter(({ value }) => value); + + return { + parameters, + requestBody, + contentType, + }; +} + +function getAcceptHeader(descriptionOperation: OperationDetails & Record) { + return descriptionOperation?.responses + ? Array.from( + new Set( + Object.values(descriptionOperation.responses).flatMap((response) => { + if (isPlainObject(response) && 'content' in response) { + return Object.keys((response as Record).content || {}); + } + return []; + }) + ) + ).join(', ') + : undefined; +} + +function transformParameters(params: Parameter[]): ParameterWithIn[] { + return (params || []) + .filter( + (parameter): parameter is ParameterWithIn & { required: true } => parameter?.required === true + ) + .map((parameter) => { + if (isParameterWithIn(parameter)) { + return { + name: parameter.name, + in: parameter.in, + value: generateExampleValue(parameter), + } as ParameterWithIn; + } + // Return undefined for non-matching parameters + return undefined; + }) + .filter((parameter): parameter is ParameterWithIn => parameter !== undefined); +} + +function getUniqueParameters(parameters: ParameterWithIn[]): ParameterWithIn[] { + const uniqParameters: Record = {}; + for (const parameter of parameters) { + if (!isParameterWithIn(parameter)) { + continue; + } + uniqParameters[(parameter.name + ':' + parameter.in).toLowerCase()] = parameter; + } + + return Object.values(uniqParameters); +} diff --git a/packages/respect-core/src/modules/description-parser/get-response-schema.ts b/packages/respect-core/src/modules/description-parser/get-response-schema.ts new file mode 100644 index 0000000000..6081a55036 --- /dev/null +++ b/packages/respect-core/src/modules/description-parser/get-response-schema.ts @@ -0,0 +1,15 @@ +export function getResponseSchema({ + statusCode, + contentType, + descriptionResponses, +}: { + statusCode: number; + contentType: string; + descriptionResponses?: Record; +}) { + if (!descriptionResponses) return undefined; + + const response = descriptionResponses[statusCode] || descriptionResponses.default; + + return response?.content?.[contentType]?.schema; +} diff --git a/packages/respect-core/src/modules/description-parser/index.ts b/packages/respect-core/src/modules/description-parser/index.ts new file mode 100644 index 0000000000..ee3002aa83 --- /dev/null +++ b/packages/respect-core/src/modules/description-parser/index.ts @@ -0,0 +1,9 @@ +export * from './bundle-openapi'; +export * from './get-request-data-from-openapi'; +export * from './get-operation-from-description'; +export * from './extract-first-example'; +export * from './get-operation-by-id'; +export * from './get-operation-by-path'; +export * from './remove-write-only-properties'; +export * from './get-response-schema'; +export * from './get-request-body-schema'; diff --git a/packages/respect-core/src/modules/description-parser/remove-write-only-properties.ts b/packages/respect-core/src/modules/description-parser/remove-write-only-properties.ts new file mode 100644 index 0000000000..7050cda7a3 --- /dev/null +++ b/packages/respect-core/src/modules/description-parser/remove-write-only-properties.ts @@ -0,0 +1,46 @@ +import type { JSONSchemaType } from '@redocly/ajv/dist/2020'; + +export function removeWriteOnlyProperties(schema: JSONSchemaType): JSONSchemaType { + const schemaCopy = JSON.parse(JSON.stringify(schema)); + + function filterWriteOnlyProps(schema: JSONSchemaType) { + if (schema.type === 'object') { + if (schema.properties) { + for (const key in schema.properties) { + if (schema.properties[key].writeOnly) { + delete schema.properties[key]; + if (schema.required) { + schema.required = schema.required.filter((prop: string) => prop !== key); + } + } else { + filterWriteOnlyProps(schema.properties[key] as JSONSchemaType); + } + } + } + if (schema.additionalProperties && typeof schema.additionalProperties === 'object') { + filterWriteOnlyProps(schema.additionalProperties as JSONSchemaType); + } + } else if (schema.type === 'array' && schema.items) { + filterWriteOnlyProps(schema.items as JSONSchemaType); + } + + if (schema.oneOf) { + schema.oneOf = schema.oneOf.map((subSchema: JSONSchemaType) => + filterWriteOnlyProps(subSchema as JSONSchemaType) + ); + } + if (schema.allOf) { + schema.allOf = schema.allOf.map((subSchema: JSONSchemaType) => + filterWriteOnlyProps(subSchema as JSONSchemaType) + ); + } + if (schema.anyOf) { + schema.anyOf = schema.anyOf.map((subSchema: JSONSchemaType) => + filterWriteOnlyProps(subSchema as JSONSchemaType) + ); + } + return schema; + } + + return filterWriteOnlyProps(schemaCopy); +} diff --git a/packages/respect-core/src/modules/faker.ts b/packages/respect-core/src/modules/faker.ts new file mode 100644 index 0000000000..a07138112e --- /dev/null +++ b/packages/respect-core/src/modules/faker.ts @@ -0,0 +1,90 @@ +import { faker } from '@faker-js/faker'; + +interface NumberOptions { + min?: number; + max?: number; + precision?: number; +} + +interface FakeString { + email(options?: { provider?: string; domain?: string }): string; + + userName(): string; + + firstName(): string; + + lastName(): string; + + fullName(): string; + + uuid(): string; + + string(options?: { length?: number }): string; +} + +interface FakeDate { + past(): Date; + + future(): Date; +} + +interface FakeAddress { + city(): string; + + country(): string; + + zipCode(): string; + + street(): string; +} + +interface FakeNumber { + integer(options?: Omit): number; + + float(options: NumberOptions): number; +} + +export interface Faker { + address: FakeAddress; + date: FakeDate; + number: FakeNumber; + string: FakeString; +} + +export function createFaker(): Faker { + const fakeString: FakeString = { + email: ({ provider, domain = 'com' }: { provider?: string; domain?: string } = {}) => + faker.internet.email(undefined, undefined, `${provider}.${domain}`), + userName: () => faker.internet.userName(), + firstName: () => faker.name.firstName(), + lastName: () => faker.name.lastName(), + fullName: () => faker.name.fullName(), + uuid: () => faker.datatype.uuid(), + string: ({ length }: { length?: number } = {}) => faker.datatype.string(length), + }; + + const fakeDate: FakeDate = { + past: () => faker.date.past(), + future: () => faker.date.future(), + }; + + const fakeAddress: FakeAddress = { + city: () => faker.address.city(), + country: () => faker.address.country(), + zipCode: () => faker.address.zipCode(), + street: () => faker.address.street(), + }; + + const fakeNumber: FakeNumber = { + integer: ({ min, max }: Omit = {}) => + faker.datatype.number({ min, max, precision: 1 }), + float: (options: NumberOptions) => faker.datatype.float(options), + }; + + return { + address: fakeAddress, + date: fakeDate, + number: fakeNumber, + string: fakeString, + }; +} diff --git a/packages/respect-core/src/modules/flow-runner/call-api-and-analyze-results.ts b/packages/respect-core/src/modules/flow-runner/call-api-and-analyze-results.ts new file mode 100644 index 0000000000..43636aede9 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/call-api-and-analyze-results.ts @@ -0,0 +1,141 @@ +import { checkCriteria } from './success-criteria'; +import { checkSchema } from './schema'; +import { CHECKS } from '../checks'; +import { createRuntimeExpressionCtx } from './context'; +import { evaluateRuntimeExpressionPayload } from '../runtime-expressions'; + +import type { RequestData } from './prepare-request'; +import type { TestContext, Step } from '../../types'; +import type { OperationDetails } from '../description-parser'; +import type { ParameterWithIn } from '../config-parser'; + +// TODO: rename +export type ResultObject = { + ctx: TestContext; + workflowName: string; + step: Step; + requestData: { + serverUrl?: { + url: string; + // TODO: support variables + }; + path: string; + method: string; + parameters: ParameterWithIn[]; + requestBody: any; + openapiOperation?: OperationDetails & Record; + }; +}; + +// TODO: split into two functions +export async function callAPIAndAnalyzeResults({ + ctx, + workflowName, + step, + requestData, +}: { + ctx: TestContext; + workflowName: string; + step: Step; + requestData: RequestData; +}) { + // clear checks in case of retry + step.checks = []; + + const checksResult = { + successCriteriaCheck: true, + expectCheck: true, + networkCheck: true, + }; + + try { + step.response = await ctx.apiClient.fetchResult(ctx, requestData); + } catch (error: any) { + step.checks.push({ + name: CHECKS.NETWORK_ERROR, + pass: false, + message: error.message, + severity: ctx.severity['NETWORK_ERROR'], + }); + checksResult.networkCheck = false; + return checksResult; + } + + const request = ctx.$workflows[workflowName].steps[step.stepId].request; + + // store step level outputs + if (step.outputs) { + const runtimeExpressionContext = createRuntimeExpressionCtx({ + ctx: { + ...ctx, + ...{ + $request: request, + $response: step.response, + $inputs: ctx.$workflows[workflowName].inputs, + }, + }, + workflowId: workflowName, + step, + }); + + if (step.outputs) { + for (const outputKey of Object.keys(step.outputs)) { + step.outputs[outputKey] = evaluateRuntimeExpressionPayload({ + payload: step.outputs[outputKey], + context: runtimeExpressionContext, + }); + } + } + } + + step.verboseLog = ctx.apiClient.getVerboseResponseLogs(); + + if (step.successCriteria) { + const successCriteriaChecks = checkCriteria({ + workflowId: workflowName, + step, + criteria: step.successCriteria, + ctx: { + ...ctx, + ...{ + $request: request, + $response: step.response, + $inputs: ctx.$workflows[workflowName].inputs, + }, + }, + }); + + checksResult.successCriteriaCheck = successCriteriaChecks.every((check) => check.pass); + step.checks.push(...successCriteriaChecks); + } + + const schemaChecks = checkSchema({ + stepCallCtx: { + $request: request, + $response: step.response, + $inputs: ctx.$workflows[workflowName].inputs, + }, + descriptionOperation: requestData.openapiOperation, + ctx, + }); + + if (schemaChecks.length) { + checksResult.expectCheck = schemaChecks.every((check) => check.pass); + step.checks.push(...schemaChecks); + } + + // save local $steps context + ctx.$steps[step.stepId] = { + outputs: ctx.$steps[step.stepId].outputs + ? { ...ctx.$steps[step.stepId].outputs, ...step.outputs } + : step.outputs, + }; + // save $workflows context + ctx.$workflows[workflowName].steps[step.stepId] = { + outputs: ctx.$steps[step.stepId].outputs, + request, + response: step.response, + }; + + return checksResult; +} diff --git a/packages/respect-core/src/modules/flow-runner/context/create-runtime-expression-ctx.ts b/packages/respect-core/src/modules/flow-runner/context/create-runtime-expression-ctx.ts new file mode 100644 index 0000000000..784902f047 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/context/create-runtime-expression-ctx.ts @@ -0,0 +1,65 @@ +import type { RuntimeExpressionContext, Step, TestContext } from '../../../types'; +// import type { OperationDetails } from '../../description-parser'; + +export function createRuntimeExpressionCtx({ + ctx, + workflowId, + step, +}: { + ctx: TestContext; + workflowId?: string; + step?: Step; +}): RuntimeExpressionContext { + if (!step || !workflowId) { + return { + $statusCode: undefined, + $method: '', + $response: undefined, + $url: '', + $request: undefined, + $outputs: ctx.$outputs, + $steps: ctx.$steps, + $workflows: ctx.$workflows, + $inputs: ctx.$inputs, + $sourceDescriptions: ctx.$sourceDescriptions, + $components: ctx.$components, + $faker: ctx.$faker, + }; + } + + const request = ctx.$workflows[workflowId].steps[step.stepId].request; + const requestPathParameters = request?.path; + const requestQueryParameters = request?.query; + const requestBody = request?.body || {}; + const requestHeaders = request?.header; + + const method = request?.method; + const resultStatusCode = step.response?.statusCode; + const responseHeaders = step.response?.header || {}; + const responseBody = step.response?.body || {}; + + return { + $statusCode: resultStatusCode, + $method: method, + $response: { + header: responseHeaders, + body: responseBody, + path: requestPathParameters, + query: requestQueryParameters, + }, + $url: request && request.url, + $request: { + header: requestHeaders, + body: requestBody, + path: requestPathParameters, + query: requestQueryParameters, + }, + $outputs: ctx.$outputs, + $steps: ctx.$steps, + $workflows: ctx.$workflows, + $inputs: ctx.$inputs, + $sourceDescriptions: ctx.$sourceDescriptions, + $components: ctx.$components, + $faker: ctx.$faker, + }; +} diff --git a/packages/respect-core/src/modules/flow-runner/context/create-test-context.ts b/packages/respect-core/src/modules/flow-runner/context/create-test-context.ts new file mode 100644 index 0000000000..4e9dde53c9 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/context/create-test-context.ts @@ -0,0 +1,121 @@ +import { dirname, resolve } from 'node:path'; +import { + type TestDescription, + type AppOptions, + type TestContext, + type InputSchema, +} from '../../../types'; +import { type ApiFetcher } from '../../../utils/api-fetcher'; +import { bundleOpenApi } from '../../description-parser'; +import { createFaker } from '../../faker'; +import { infoSubstitute } from '../../test-config-generator'; +import { formatCliInputs } from '../inputs'; +import { bundleArazzo } from '../get-test-description-from-file'; +import { readEnvVariables } from '../read-env-variables'; +import { getNestedValue } from '../../../utils/get-nested-value'; +import { getPublicWorkflows } from './set-public-workflows'; +import { resolveMtlsCertificates } from '../../../utils/mtls/resolve-mtls-certificates'; +import { resolveSeverityConfiguration } from '../../checks'; + +const faker = createFaker(); + +interface Descriptions { + [key: string]: any; +} + +export async function createTestContext( + testDescription: TestDescription, + options: AppOptions, + apiClient: ApiFetcher +): Promise { + const sourceDescriptions = testDescription?.sourceDescriptions; + + const bundledDescriptions = {} as Descriptions; + + if (sourceDescriptions) { + await Promise.all( + sourceDescriptions.map(async (sourceDescription) => { + if (sourceDescription.type === 'openapi') { + bundledDescriptions[sourceDescription.name] = await bundleOpenApi( + sourceDescription.url, + options.workflowPath + ); + } else if (sourceDescription.type === 'arazzo') { + const { url: sourceDescriptionPath, name } = sourceDescription; + const filePath = resolve(dirname(options.workflowPath), sourceDescriptionPath); + const bundledTestDescription = await bundleArazzo(filePath); + + bundledDescriptions[name] = bundledTestDescription; + } + }) + ); + } + + for (const workflow of testDescription.workflows || []) { + for (const step of workflow.steps) { + step.checks = []; // we are mutating the copy of the arazzo file + } + } + + const ctx: TestContext = { + $response: undefined, + $request: undefined, + $inputs: { env: {} }, + $faker: faker, + $sourceDescriptions: bundledDescriptions, + $workflows: getPublicWorkflows({ + workflows: testDescription.workflows || [], + inputs: formatCliInputs(options?.input), + env: readEnvVariables(options.workflowPath) || {}, + }), + $steps: {}, + $components: testDescription.components || {}, + $outputs: {}, + + workflows: testDescription.workflows || [], + harLogs: {}, + options, + testDescription, + info: testDescription.info || infoSubstitute, + arazzo: testDescription.arazzo || '', + sourceDescriptions: testDescription.sourceDescriptions || [], + secretFields: new Set(), + mtlsCerts: + options.mutualTls?.clientCert || options.mutualTls?.clientKey || options.mutualTls?.caCert + ? resolveMtlsCertificates(options.mutualTls) + : undefined, + severity: resolveSeverityConfiguration(options.severity), + apiClient, + }; + + // Collect all secret fields from the input schema and the workflow inputs + for (const workflow of testDescription.workflows || []) { + if (workflow.inputs) { + collectSecretFields(ctx, workflow.inputs, ctx.$workflows[workflow.workflowId].inputs); + } + } + + return ctx; +} + +export function collectSecretFields( + ctx: TestContext, + schema: InputSchema | undefined, + inputs: Record | undefined, + path: string[] = [] +) { + if (!schema || !inputs) return; + + const inputValue = getNestedValue(inputs, path); + + if (schema.format === 'password' && inputValue) { + ctx.secretFields?.add(inputValue); + } + + if (schema.properties) { + Object.entries(schema.properties).forEach(([key, value]: [string, any]) => { + const currentPath = [...path, key]; + collectSecretFields(ctx, value, inputs, currentPath); + }); + } +} diff --git a/packages/respect-core/src/modules/flow-runner/context/index.ts b/packages/respect-core/src/modules/flow-runner/context/index.ts new file mode 100644 index 0000000000..6470b6c465 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/context/index.ts @@ -0,0 +1,4 @@ +export * from './create-test-context'; +export * from './set-public-steps'; +export * from './set-public-workflows'; +export * from './create-runtime-expression-ctx'; diff --git a/packages/respect-core/src/modules/flow-runner/context/set-public-steps.ts b/packages/respect-core/src/modules/flow-runner/context/set-public-steps.ts new file mode 100644 index 0000000000..52c1d0da6b --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/context/set-public-steps.ts @@ -0,0 +1,11 @@ +import type { Step } from '../../../types'; + +export function getPublicSteps(steps: Step[]): Record { + const publicSteps = {} as Record; + + for (const step of steps) { + publicSteps[step.stepId] = {}; + } + + return publicSteps; +} diff --git a/packages/respect-core/src/modules/flow-runner/context/set-public-workflows.ts b/packages/respect-core/src/modules/flow-runner/context/set-public-workflows.ts new file mode 100644 index 0000000000..0da181df97 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/context/set-public-workflows.ts @@ -0,0 +1,47 @@ +import { resolveInputValuesToSchema } from '../inputs'; +import { getPublicSteps } from './set-public-steps'; + +import type { Workflow, PublicWorkflow, InputSchema } from '../../../types'; + +export function getPublicWorkflows({ + workflows, + inputs, + env = {}, +}: { + workflows: Workflow[]; + inputs: Record; + env: Record; +}): Record { + const publicWorkflows = {} as Record; + + for (const workflow of workflows) { + const workflowInputSchema = workflow.inputs; + + let resolvedInputs = {}; + let resolvedDotEnvInputs = {}; + + if (workflowInputSchema) { + resolvedInputs = resolveInputValuesToSchema(inputs, workflowInputSchema as InputSchema); + } + + if (workflowInputSchema?.properties?.env) { + resolvedDotEnvInputs = resolveInputValuesToSchema( + env || {}, + workflowInputSchema.properties.env as InputSchema + ); + } + + const mergedInputs = + Object.keys(resolvedDotEnvInputs).length > 0 + ? { ...resolvedInputs, env: resolvedDotEnvInputs } + : resolvedInputs; + + publicWorkflows[workflow.workflowId] = { + steps: getPublicSteps(workflow.steps || []), + inputs: workflowInputSchema ? mergedInputs : undefined, + outputs: workflow.outputs, + }; + } + + return publicWorkflows; +} diff --git a/packages/respect-core/src/modules/flow-runner/error-message-matcher.ts b/packages/respect-core/src/modules/flow-runner/error-message-matcher.ts new file mode 100644 index 0000000000..6b1954e98c --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/error-message-matcher.ts @@ -0,0 +1,11 @@ +import { bold } from 'colorette'; + +export function errorMessageMatcher( + hint: string, // assertion returned from call to matcherHint + generic: string, // condition which correct value must fulfill + specific?: string // incorrect value returned from call to printWithType +): string { + return `${hint}\n\n${bold('Matcher error')}: ${generic}${ + typeof specific === 'string' ? `\n\n${specific}` : '' + }`; +} diff --git a/packages/respect-core/src/modules/flow-runner/get-server-url.ts b/packages/respect-core/src/modules/flow-runner/get-server-url.ts new file mode 100644 index 0000000000..5f544b5608 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/get-server-url.ts @@ -0,0 +1,95 @@ +import { getValueFromContext } from '../config-parser'; +import { formatCliInputs } from './inputs'; + +import type { ExtendedOperation, TestContext } from '../../types'; +import type { OperationDetails } from '../description-parser'; + +export type GetServerUrlInput = { + ctx: TestContext; + descriptionName?: string; + openapiOperation?: (OperationDetails & Record) | undefined; + xOperation?: ExtendedOperation; +}; + +export function getServerUrl({ + ctx, + descriptionName, + openapiOperation, + xOperation, +}: GetServerUrlInput): { url: string } | undefined { + if (!descriptionName && xOperation?.url) { + return { url: xOperation?.url }; + } + + // Handle server overrides from command line `server` option + if (ctx.options?.server && descriptionName) { + const serverOverrides = formatCliInputs(ctx.options.server); + if (serverOverrides[descriptionName]) { + return { url: serverOverrides[descriptionName] }; + } + } + + const sourceDescription = ctx.sourceDescriptions?.find((sd) => sd.name === descriptionName); + + if ( + sourceDescription && + sourceDescription.type === 'openapi' && + sourceDescription['x-serverUrl'] + ) { + if (sourceDescription['x-serverUrl'].startsWith('http')) { + return { + url: sourceDescription['x-serverUrl'], + }; + } + + return { + url: getValueFromContext('$' + `sourceDescriptions.${descriptionName}.servers.0.url`, ctx), + }; + } + + if (openapiOperation?.servers?.[0]) { + const activeSourceDescription = ctx.sourceDescriptions?.find( + (sd) => sd.name === openapiOperation.sourceDescriptionName + ); + let serverUrlOverride; + + if (ctx.sourceDescriptions?.length === 1 && ctx.sourceDescriptions[0]) { + serverUrlOverride = + 'x-serverUrl' in ctx.sourceDescriptions[0] + ? ctx.sourceDescriptions[0]['x-serverUrl'] + : undefined; + } else if (activeSourceDescription) { + serverUrlOverride = + 'x-serverUrl' in activeSourceDescription + ? activeSourceDescription['x-serverUrl'] + : undefined; + } + + return serverUrlOverride ? { url: serverUrlOverride } : openapiOperation.servers[0]; + } + + if (!descriptionName && ctx?.sourceDescriptions && ctx.sourceDescriptions.length === 1) { + const sourceDescription = ctx.sourceDescriptions[0]; + + let serverUrl = ''; + if ('x-serverUrl' in sourceDescription && sourceDescription['x-serverUrl']) { + serverUrl = sourceDescription['x-serverUrl']; + } else { + serverUrl = ctx.$sourceDescriptions[sourceDescription.name]?.servers[0]?.url || undefined; + } + + return serverUrl ? { url: serverUrl } : undefined; + } + + if ( + !descriptionName || + !ctx.$sourceDescriptions || + !ctx.$sourceDescriptions[descriptionName] || + !ctx.$sourceDescriptions[descriptionName].servers.length + ) { + return undefined; + } + + // Get first available server url from the description + return ctx.$sourceDescriptions[descriptionName].servers[0]; +} diff --git a/packages/respect-core/src/modules/flow-runner/get-test-description-from-file.ts b/packages/respect-core/src/modules/flow-runner/get-test-description-from-file.ts new file mode 100644 index 0000000000..6b24257734 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/get-test-description-from-file.ts @@ -0,0 +1,66 @@ +import { bold, red } from 'colorette'; +import { getTotals, formatProblems, lint, bundle, createConfig } from '@redocly/openapi-core'; +import * as path from 'node:path'; +import { existsSync } from 'node:fs'; +import { type TestDescription } from '../../types'; +import { printConfigLintTotals } from '../../utils/cli-outputs'; +import { version } from '../../../package.json'; +import { isTestFile } from '../../utils/file'; +import { readYaml } from '../../utils/yaml'; + +export async function bundleArazzo(filePath: string) { + const fileName = path.basename(filePath); + + if (!fileName) { + throw new Error('Invalid file name'); + } + + if (!existsSync(filePath)) { + const relativePath = path.relative(process.cwd(), filePath); + throw new Error( + `Could not find source description file '${fileName}' at path '${relativePath}'` + ); + } + + const fileContent = await readYaml(filePath); + + if (!isTestFile(fileName, fileContent)) { + throw new Error( + `No test files found. File ${fileName} does not follows naming pattern "*.[yaml | yml | json]" or have not valid "Arazzo" description.` + ); + } + + const config = await createConfig({ + extends: ['recommended-strict'], + arazzo1Rules: { + 'no-criteria-xpath': 'error', + 'spot-supported-versions': 'error', + }, + }); + + const lintProblems = await lint({ + ref: filePath, + config, + }); + + if (lintProblems.length) { + const fileTotals = getTotals(lintProblems); + + formatProblems(lintProblems, { + totals: fileTotals, + version, + }); + + printConfigLintTotals(fileTotals); + + throw new Error(`${red('Invalid file configuration')} ${bold(fileName)}`); + } + + const bundledDocument = await bundle({ + ref: filePath, + config, + dereference: true, + }); + + return (bundledDocument.bundle.parsed || {}) as TestDescription; +} diff --git a/packages/respect-core/src/modules/flow-runner/get-workflows-to-run.ts b/packages/respect-core/src/modules/flow-runner/get-workflows-to-run.ts new file mode 100644 index 0000000000..28ec8a2735 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/get-workflows-to-run.ts @@ -0,0 +1,64 @@ +import { red, yellow } from 'colorette'; +import { DefaultLogger } from '../../utils/logger/logger'; + +import type { Workflow } from '../../types'; + +const logger = DefaultLogger.getInstance(); + +export function getWorkflowsToRun( + workflows: Workflow[], + workflowsToRun: string[] | undefined, + workflowsToSkip: string[] | undefined +): Workflow[] { + let filteredWorkflows: Workflow[] = []; + + if (workflowsToRun && workflowsToRun.length) { + filteredWorkflows = filterWorkflowsToRun(workflows, workflowsToRun); + } else if (workflowsToSkip && workflowsToSkip.length) { + filteredWorkflows = filterWorkflowsToSkip(workflows, workflowsToSkip); + } else { + filteredWorkflows = workflows; + } + + return filteredWorkflows; +} + +function filterWorkflowsToSkip(workflows: Workflow[], workflowsToSkip: string[]) { + const workflowsToRun = workflows.filter( + (workflow) => !workflowsToSkip.includes(workflow.workflowId) + ); + + if (!workflowsToRun.length) { + logger.log(`${red('All workflows are skipped')}`); + logger.printNewLine(); + return []; + } + + logger.log(`${yellow(`Following workflows are skipped: ${workflowsToSkip.join(', ')}`)}`); + logger.printNewLine(); + + return workflowsToRun; +} + +function filterWorkflowsToRun(workflows: Workflow[], workflowsToRun: string[]) { + const filteredWorkflows = filterWorkflowsByIds(workflows, workflowsToRun); + + if (!filteredWorkflows.length) { + throw new Error(`Following workflows don't exist: ${workflowsToRun.join(', ')}`); + } + + if (filteredWorkflows.length === workflowsToRun.length) { + return filteredWorkflows; + } else { + const notExistingWorkflows = workflowsToRun.filter((workflowId) => { + return !workflows.find((workflow) => workflow.workflowId === workflowId); + }); + + logger.log(`Following workflows don't exist: ${notExistingWorkflows.join(', ')}`); + return filteredWorkflows; + } +} + +function filterWorkflowsByIds(workflows: Workflow[], workflowIds: string[]) { + return workflows.filter((workflow) => workflowIds.includes(workflow.workflowId)); +} diff --git a/packages/respect-core/src/modules/flow-runner/index.ts b/packages/respect-core/src/modules/flow-runner/index.ts new file mode 100644 index 0000000000..061e452a7c --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/index.ts @@ -0,0 +1,15 @@ +export * from './runner'; +export * from './context'; +export * from './get-workflows-to-run'; +export * from './schema'; +export * from './inputs'; +export * from './outputs'; +export * from '../checks'; +export * from './error-message-matcher'; +export * from './success-criteria'; +export * from './get-server-url'; +export * from './call-api-and-analyze-results'; +export * from './run-step'; +export * from './prepare-request'; +export * from './read-env-variables'; +export * from './resolve-running-workflows'; diff --git a/packages/respect-core/src/modules/flow-runner/inputs/format-cli-inputs.ts b/packages/respect-core/src/modules/flow-runner/inputs/format-cli-inputs.ts new file mode 100644 index 0000000000..3c7d0e6636 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/inputs/format-cli-inputs.ts @@ -0,0 +1,47 @@ +export function formatCliInputs(input: string | string[] | undefined): Record { + if (!input) { + return {}; + } + + if (Array.isArray(input)) { + return input.reduce((result, param) => { + const parsed = parseParam(param); + return { ...result, ...parsed }; + }, {} as Record); + } + + return parseParam(input); +} + +function parseParam(param: string): Record { + try { + const parsedObject = JSON.parse(param); + + if (typeof parsedObject === 'object' && parsedObject !== null) { + return parsedObject; + } + } catch { + // do nothing + } + + if (typeof param === 'string') { + // Handle comma-separated key-value pairs + if (param.includes(',')) { + return param.split(',').reduce((acc, pair) => { + const [key, value] = pair.split('='); + if (key && value) { + acc[key.trim()] = value.trim(); + } + return acc; + }, {} as Record); + } + + // Handle single key-value pair + if (param.includes('=')) { + const [key, value] = param.split('='); + return { [key.trim()]: value.trim() }; + } + } + + return {}; +} diff --git a/packages/respect-core/src/modules/flow-runner/inputs/index.ts b/packages/respect-core/src/modules/flow-runner/inputs/index.ts new file mode 100644 index 0000000000..187c16c295 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/inputs/index.ts @@ -0,0 +1,2 @@ +export * from './format-cli-inputs'; +export * from './map-input-values-to-schema'; diff --git a/packages/respect-core/src/modules/flow-runner/inputs/map-input-values-to-schema.ts b/packages/respect-core/src/modules/flow-runner/inputs/map-input-values-to-schema.ts new file mode 100644 index 0000000000..6c21138bd7 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/inputs/map-input-values-to-schema.ts @@ -0,0 +1,34 @@ +import Ajv from '@redocly/ajv/dist/2020'; + +import type { InputSchema } from '../../../types'; + +type MappedValue = { + [key: string]: any; +}; + +export function resolveInputValuesToSchema(value: any, schema: InputSchema): MappedValue { + if (!schema || Object.keys(schema).length === 0) { + return {}; + } + + const ajv = new Ajv({ + useDefaults: true, + removeAdditional: 'all', + coerceTypes: true, + strictTypes: false, + }); + + // Add custom formats + ajv.addFormat('password', true); + ajv.addFormat('int32', true); + ajv.addFormat('int64', true); + ajv.addFormat('float', true); + ajv.addFormat('double', true); + + const validate = ajv.compile(schema); + const result = { ...value }; + + validate(result); + + return result; +} diff --git a/packages/respect-core/src/modules/flow-runner/outputs/index.ts b/packages/respect-core/src/modules/flow-runner/outputs/index.ts new file mode 100644 index 0000000000..193409e2a1 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/outputs/index.ts @@ -0,0 +1 @@ +export * from './print-message'; diff --git a/packages/respect-core/src/modules/flow-runner/outputs/print-message.ts b/packages/respect-core/src/modules/flow-runner/outputs/print-message.ts new file mode 100644 index 0000000000..e227415bd9 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/outputs/print-message.ts @@ -0,0 +1,14 @@ +import { green, red } from 'colorette'; + +export const EXPECTED_COLOR = green; +export const RECEIVED_COLOR = red; +export const EXPECTED_LABEL = 'Expected'; +export const RECEIVED_LABEL = 'Received'; + +export function printReceived(received: any): string { + return RECEIVED_COLOR(JSON.stringify(received, null, 2)); +} + +export function printExpected(expected: any): string { + return EXPECTED_COLOR(JSON.stringify(expected, null, 2)); +} diff --git a/packages/respect-core/src/modules/flow-runner/prepare-request.ts b/packages/respect-core/src/modules/flow-runner/prepare-request.ts new file mode 100644 index 0000000000..14b591ff89 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/prepare-request.ts @@ -0,0 +1,226 @@ +import { + getOperationFromDescriptionBySource, + getRequestBodySchema, + getRequestDataFromOpenApi, +} from '../description-parser'; +import { + parseRequestBody, + resolveReusableComponentItem, + isParameterWithIn, +} from '../config-parser'; +import { getServerUrl } from './get-server-url'; +import { createRuntimeExpressionCtx, collectSecretFields } from './context'; +import { evaluateRuntimeExpressionPayload } from '../runtime-expressions'; + +import type { ParameterWithIn } from '../config-parser'; +import type { TestContext, Step, Parameter, PublicStep } from '../../types'; +import type { OperationDetails } from '../description-parser'; + +export type RequestData = { + serverUrl?: { + url: string; + // todo: support variables + }; + path: string; + method: string; + parameters: ParameterWithIn[]; + requestBody: any; + openapiOperation?: OperationDetails & Record; +}; + +export async function prepareRequest( + ctx: TestContext, + step: Step, + workflowName: string +): Promise { + const { stepId, operationId, operationPath, 'x-operation': xOperation } = step; + + const activeWorkflow = ctx.workflows.find((workflow) => workflow.workflowId === workflowName); + const workflowLevelParameters = (activeWorkflow && activeWorkflow.parameters) || []; + + const openapiOperation = xOperation + ? undefined + : getOperationFromDescriptionBySource( + { + operationId, + operationPath, + }, + ctx + ); + + let path = ''; + let method; + const serverUrl: { url: string; descriptionName?: string } | undefined = getServerUrl({ + ctx, + descriptionName: openapiOperation?.descriptionName, + openapiOperation, + xOperation, + }); + + if (xOperation) { + method = xOperation.method; + } else if (openapiOperation) { + path = openapiOperation?.path; + method = openapiOperation?.method; + } else { + // this should never happen, making typescript happy + throw new Error('No operation found'); + } + + if (!serverUrl && !path.includes('http')) { + throw new Error('No servers found in API description'); + } + if (!method) { + throw new Error('"method" is required to make a request'); + } + + const requestDataFromOpenAPI = openapiOperation && getRequestDataFromOpenApi(openapiOperation); + + const { + payload: stepRequestBodyPayload, + // encoding: stepRequestBodyEncoding, + contentType: stepRequestBodyContentType, + } = await parseRequestBody(step['requestBody']); + + const requestBody = stepRequestBodyPayload || requestDataFromOpenAPI?.requestBody; + const contentType = stepRequestBodyContentType || requestDataFromOpenAPI?.contentType; + const parameters = joinParameters( + // order is important here, the last one wins + typeof requestBody === 'object' + ? [{ in: 'header', name: 'content-type', value: 'application/json' }] + : [], + requestDataFromOpenAPI?.parameters || [], + resolveParameters(workflowLevelParameters, ctx), + stepRequestBodyContentType + ? [{ in: 'header', name: 'content-type', value: stepRequestBodyContentType }] + : [], + resolveParameters(step.parameters || [], ctx) + ); + + // save local $steps context before evaluating runtime expressions + if (!ctx.$steps[stepId]) { + ctx.$steps[stepId] = {} as PublicStep; + } + // save local $workflows context + if (!ctx.$workflows[workflowName].steps[stepId]) { + ctx.$workflows[workflowName].steps[stepId] = {} as PublicStep; + } + ctx.$workflows[workflowName].steps[stepId].request = { + body: requestBody, + header: groupParametersValuesByName(parameters, 'header'), + path: groupParametersValuesByName(parameters, 'path'), + query: groupParametersValuesByName(parameters, 'query'), + url: serverUrl?.url && path ? `${serverUrl?.url}${path}` : serverUrl?.url, + method, + }; + + const ctxWithInputs = { + ...ctx, + $inputs: { + ...(ctx.$inputs || {}), + ...(ctx.$workflows[workflowName]?.inputs || {}), + }, + }; + + const expressionContext = createRuntimeExpressionCtx({ + ctx: ctxWithInputs, + workflowId: workflowName, + step, + }); + + const evaluatedParameters = parameters.map((parameter) => { + return { + ...parameter, + value: evaluateRuntimeExpressionPayload({ + payload: parameter.value, + context: expressionContext, + // contentType, + }), + }; + }); + + for (const param of openapiOperation?.parameters || []) { + const { schema, name } = param; + collectSecretFields(ctx, schema, groupParametersValuesByName(parameters, param.in), [name]); + } + + const evaluatedBody = evaluateRuntimeExpressionPayload({ + payload: requestBody, + context: expressionContext, + contentType, + }); + + if (contentType && openapiOperation?.requestBody) { + const requestBodySchema = getRequestBodySchema(contentType, openapiOperation); + collectSecretFields(ctx, requestBodySchema, requestBody); + } + + // Supporting temporal extension of query method https://httpwg.org/http-extensions/draft-ietf-httpbis-safe-method-w-body.html + if (method?.toLowerCase() === 'x-query') { + method = 'query' as const; + } + + return { + serverUrl, + path, + method, + parameters: extractCookieParametersFromHeaderParameters(evaluatedParameters), + requestBody: evaluatedBody, + openapiOperation, + }; +} + +function joinParameters(...parameters: ParameterWithIn[][]): ParameterWithIn[] { + const parametersWithNames = parameters.flat().filter((param) => 'name' in param); + + const parameterMap = parametersWithNames.reduce((map, param) => { + map[param.name] = param; + return map; + }, {} as { [key: string]: ParameterWithIn }); + + return Object.values(parameterMap); +} + +function groupParametersValuesByName( + parameters: ParameterWithIn[], + inValue: 'header' | 'query' | 'path' | 'cookie' +): Record { + return parameters.reduce((acc, param) => { + if (param.in === inValue && 'name' in param) { + acc[param.in === 'header' ? param.name.toLowerCase() : param.name] = param.value; + } + return acc; + }, {} as Record); +} + +function resolveParameters(parameters: Parameter[], ctx: TestContext): ParameterWithIn[] { + return parameters + .map((parameter) => { + const resolvedParameter = resolveReusableComponentItem(parameter, ctx); + if (!isParameterWithIn(resolvedParameter)) { + return undefined; + } + return resolvedParameter; + }) + .filter((parameter) => parameter !== undefined); +} + +function extractCookieParametersFromHeaderParameters( + parameters: ParameterWithIn[] +): ParameterWithIn[] { + const result = []; + for (const parameter of parameters) { + if (parameter.in === 'header' && parameter.name.toLowerCase() === 'cookie') { + const cookieParameters = String(parameter.value) + .split(';') + .map((cookie) => { + const [key, value] = cookie.split('='); + return { name: key, value, in: 'cookie' as const }; + }); + result.push(...cookieParameters); + } else { + result.push(parameter); + } + } + return result; +} diff --git a/packages/respect-core/src/modules/flow-runner/read-env-variables.ts b/packages/respect-core/src/modules/flow-runner/read-env-variables.ts new file mode 100644 index 0000000000..97b959d637 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/read-env-variables.ts @@ -0,0 +1,22 @@ +import * as dotenv from 'dotenv'; +import * as path from 'path'; +import * as fs from 'fs'; + +export function readEnvVariables(executionFilePath?: string) { + if (executionFilePath) { + let currentDir = path.dirname(executionFilePath); + + while (currentDir !== path.resolve(currentDir, '..')) { + const envFilePath = path.join(currentDir, '.env'); + + if (fs.existsSync(envFilePath)) { + dotenv.config({ path: envFilePath }); + break; + } + + currentDir = path.resolve(currentDir, '..'); + } + } + + return process.env as Record; +} diff --git a/packages/respect-core/src/modules/flow-runner/resolve-running-workflows.ts b/packages/respect-core/src/modules/flow-runner/resolve-running-workflows.ts new file mode 100644 index 0000000000..48e53be701 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/resolve-running-workflows.ts @@ -0,0 +1,25 @@ +export function resolveRunningWorkflows( + workflows: string | string[] | undefined +): string[] | undefined { + if (!workflows) { + return undefined; + } + + if (typeof workflows === 'string') { + return workflows.includes(',') ? workflows.split(',').map((w) => w.trim()) : [workflows]; + } + + if (Array.isArray(workflows)) { + const result: string[] = []; + for (const workflow of workflows) { + if (workflow.includes(',')) { + result.push(...workflow.split(',').map((w) => w.trim())); + } else { + result.push(workflow); + } + } + return result; + } + + return undefined; +} diff --git a/packages/respect-core/src/modules/flow-runner/run-step.ts b/packages/respect-core/src/modules/flow-runner/run-step.ts new file mode 100644 index 0000000000..d424d9bffa --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/run-step.ts @@ -0,0 +1,326 @@ +import { blue, white, bold, red } from 'colorette'; +import { callAPIAndAnalyzeResults } from './call-api-and-analyze-results'; +import { checkCriteria } from './success-criteria'; +import { delay } from '../../utils/delay'; +import { CHECKS } from '../checks'; +import { runWorkflow, resolveWorkflowContext } from './runner'; +import { prepareRequest, type RequestData } from './prepare-request'; +import { printStepDetails } from '../../utils/cli-outputs'; +import { + getValueFromContext, + isParameterWithoutIn, + resolveReusableComponentItem, +} from '../config-parser'; +import { evaluateRuntimeExpressionPayload } from '../runtime-expressions'; +import { DefaultLogger } from '../../utils/logger/logger'; + +import type { + Check, + Step, + TestContext, + Parameter, + OnSuccessObject, + OnFailureObject, + RuntimeExpressionContext, + ResolvedParameter, +} from '../../types'; +import type { ParameterWithoutIn } from '../config-parser'; + +const logger = DefaultLogger.getInstance(); + +export async function runStep({ + step, + ctx, + workflowName, + parentStepId, + parentWorkflowId, + retriesLeft, +}: { + step: Step; + ctx: TestContext; + workflowName: string | undefined; + parentStepId?: string; + parentWorkflowId?: string; + retriesLeft?: number; +}): Promise<{ shouldEnd: boolean } | void> { + const workflow = ctx.workflows.find((w) => w.workflowId === workflowName); + const { stepId, onFailure, onSuccess, workflowId, parameters } = step; + + const failureActionsToRun = (onFailure || workflow?.failureActions || []).map( + (action) => resolveReusableComponentItem(action, ctx) as OnFailureObject + ); + const successActionsToRun = (onSuccess || workflow?.successActions || []).map( + (action) => resolveReusableComponentItem(action, ctx) as OnSuccessObject + ); + + const resolvedParameters = parameters?.map( + (parameter) => resolveReusableComponentItem(parameter, ctx) as ResolvedParameter + ); + + if (workflowId) { + const resolvedWorkflow = + ctx.workflows.find((w) => w.workflowId === workflowId) || + getValueFromContext(workflowId, ctx); + + if (!resolvedWorkflow) { + const failedCall: Check = { + name: CHECKS.UNEXPECTED_ERROR, + message: `Workflow ${red(workflowId)} not found.`, + pass: false, + severity: ctx.severity['UNEXPECTED_ERROR'], + }; + step.checks.push(failedCall); + return; + } + + const workflowCtx = await resolveWorkflowContext(workflowId, resolvedWorkflow, ctx); + + if (resolvedParameters && resolvedParameters.length) { + // When the step in context specifies a workflowId, then all parameters without `in` maps to workflow inputs. + const workflowInputParameters = resolvedParameters + .filter(isParameterWithoutIn) + .reduce((acc, parameter: ParameterWithoutIn) => { + // Ensure parameter is of type ParameterWithoutIn + acc[parameter.name] = getValueFromContext(parameter.value, ctx); + return acc; + }, {} as Record); + + workflowCtx.$workflows[resolvedWorkflow.workflowId].inputs = workflowInputParameters; + } + + const stepWorkflowResult = await runWorkflow({ + workflowInput: resolvedWorkflow, + ctx: workflowCtx, + parentWorkflowId: workflowId || parentWorkflowId, + parentStepId: parentStepId || stepId, + }); + + // FIXME + if (stepWorkflowResult?.steps) { + const innerSteps = stepWorkflowResult.steps as Step[]; + // merge all checks from all steps in executed workflow + step.checks = innerSteps.flatMap(({ checks }) => checks); + } + + if (step?.outputs && stepWorkflowResult?.outputs) { + try { + for (const [outputKey, outputValue] of Object.entries(step.outputs)) { + // need to partially emulate $outputs context + step.outputs[outputKey] = evaluateRuntimeExpressionPayload({ + payload: outputValue, + context: { + $outputs: stepWorkflowResult.outputs, + } as RuntimeExpressionContext, + }); + } + } catch (error: any) { + const failedCall: Check = { + name: CHECKS.UNEXPECTED_ERROR, + message: error.message, + pass: false, + severity: ctx.severity['UNEXPECTED_ERROR'], + }; + step.checks.push(failedCall); + return; + } + + // save local $steps context + ctx.$steps[stepId] = { + outputs: step?.outputs, + }; + + // save local $steps context to parent workflow + if (workflow?.workflowId) { + ctx.$workflows[workflow.workflowId].steps[stepId] = { + outputs: step?.outputs, + request: undefined, + response: undefined, + }; + } + } + + return { shouldEnd: false }; + } + + if (resolvedParameters && resolvedParameters.length) { + // When the step in context does not specify a workflowId the `in` field MUST be specified. + const parameterWithoutIn = resolvedParameters.find((parameter: Parameter) => { + const resolvedParameter = resolveReusableComponentItem(parameter, ctx) as ResolvedParameter; + return !('in' in resolvedParameter); + }); + + if (parameterWithoutIn) { + throw new Error( + `Parameter "in" is required for ${stepId} step parameter ${parameterWithoutIn.name}` + ); + } + } + + let allChecksPassed = false; + let requestData: RequestData | undefined; + + try { + if (!workflowName) { + throw new Error('Workflow name is required to run a step'); + } + + requestData = await prepareRequest(ctx, step, workflowName); + + const checksResult = await callAPIAndAnalyzeResults({ + ctx, + workflowName, + step, + requestData, + }); + + allChecksPassed = Object.values(checksResult).every((check) => check); + + // we need to handle retry action separatelly as it should replace the current step state and should not log + if (failureActionsToRun.length && !allChecksPassed) { + const result = await runActions(failureActionsToRun, ['retry']); + if (result?.retriesLeft && result.retriesLeft > 0) { + // if retriesLeft > 0, it means that the step was retried successfully and we need + // to stop output logs and return step result to the outer workflow + return result.stepResult; + } + } + } catch (e: any) { + step.verboseLog = ctx.apiClient.getVerboseResponseLogs(); + + const failedCall: Check = { + name: CHECKS.UNEXPECTED_ERROR, + message: e.message, + pass: false, + severity: ctx.severity['UNEXPECTED_ERROR'], + }; + step.checks.push(failedCall); + } + + const verboseLogs = ctx.options.verbose ? ctx.apiClient.getVerboseLogs() : undefined; + const verboseResponseLogs = ctx.options.verbose + ? ctx.apiClient.getVerboseResponseLogs() + : undefined; + const requestUrl = requestData?.path || requestData?.serverUrl?.url; + if (requestUrl) { + printStepDetails({ + testNameToDisplay: `${requestData?.method.toUpperCase()} ${white(requestUrl)}${ + step.stepId ? ` ${blue('- step')} ${white(bold(step.stepId))}` : '' + }`, + checks: step.checks, + verboseLogs, + verboseResponseLogs, + }); + } + + // onFailure handle 'goto' and 'end'. + if (failureActionsToRun.length && !allChecksPassed) { + const result = await runActions(failureActionsToRun, ['end', 'goto']); // retry is handled earlier + if (result?.shouldEnd) { + return { shouldEnd: true }; + } + } + + if (successActionsToRun.length && allChecksPassed) { + const result = await runActions(successActionsToRun, ['end', 'goto']); + if (result?.shouldEnd) { + return { shouldEnd: true }; + } + } + + // Internal function to run actions + async function runActions( + actions: OnFailureObject[] | OnSuccessObject[], + onlyTypes?: ('end' | 'goto' | 'retry')[] + ): Promise<{ + retriesLeft?: number; + shouldEnd?: boolean; + stepResult?: { shouldEnd: boolean } | void; + } | void> { + for (const action of actions) { + const { type, criteria } = action; + + if (action.workflowId && action.stepId) { + throw new Error( + `Cannot use both workflowId: ${action.workflowId} and stepId: ${action.stepId} in ${action.type} action` + ); + } + + const matchesCriteria = checkCriteria({ + workflowId: workflowName, + step, + criteria, + ctx, + }).every((check) => check.pass); + + if (matchesCriteria) { + if (onlyTypes && !onlyTypes.includes(type)) { + break; + } + + const targetWorkflow = action.workflowId + ? getValueFromContext(action.workflowId, ctx) + : undefined; + const targetCtx = action.workflowId + ? await resolveWorkflowContext(action.workflowId, targetWorkflow, ctx) + : ctx; + const targetStep = action.stepId ? step.stepId : undefined; + + if (type === 'retry') { + const { retryAfter, retryLimit = 0 } = action; + retriesLeft = retriesLeft ?? retryLimit; + if (retriesLeft === 0) { + return { retriesLeft: 0, shouldEnd: false }; + } + await delay(retryAfter); + logger.log( + `\n Retrying step ${blue(stepId)} attempt # ${retryLimit - retriesLeft + 1}\n` + ); + if (targetWorkflow) { + await runWorkflow({ + workflowInput: targetWorkflow, + ctx: targetCtx, + parentWorkflowId: workflowName, + fromStepId: targetStep, + }); + } else if (targetStep) { + const stepToRun = workflow?.steps.find((s) => s.stepId === targetStep) as Step; + if (!stepToRun) { + throw new Error(`Step ${targetStep} not found in workflow ${workflowName}`); + } + await runStep({ + step: stepToRun, + ctx: targetCtx, + workflowName: workflowName as string, + }); + } + return { + stepResult: await runStep({ + step, + ctx, + workflowName, + parentStepId: stepId, + parentWorkflowId: workflowName, + retriesLeft: retriesLeft - 1, + }), + retriesLeft, + }; + } else if (type === 'end') { + return { shouldEnd: true }; + } else if (type === 'goto') { + if (!targetWorkflow && !targetStep) { + throw new Error('Either workflowId or stepId must be provided in goto action'); + } + await runWorkflow({ + workflowInput: targetWorkflow || workflow, + ctx: targetCtx, + parentWorkflowId: workflowName, + fromStepId: targetStep, + }); + return { shouldEnd: true }; + } + // stop at first matching action + break; + } + } + } +} diff --git a/packages/respect-core/src/modules/flow-runner/runner.ts b/packages/respect-core/src/modules/flow-runner/runner.ts new file mode 100644 index 0000000000..1367d46e59 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/runner.ts @@ -0,0 +1,313 @@ +import { blue, green } from 'colorette'; +import { basename, dirname, resolve } from 'node:path'; +import { writeFileSync } from 'node:fs'; +import { createHarLog } from '../../utils/har-logs'; +import { ApiFetcher } from '../../utils/api-fetcher'; +import { createTestContext } from './context/create-test-context'; +import { getValueFromContext } from '../config-parser'; +import { getWorkflowsToRun } from './get-workflows-to-run'; +import { runStep } from './run-step'; +import { + printWorkflowSeparator, + printStepWorkflowSeparator, + printDependentWorkflowSeparator, + indent, +} from '../../utils/cli-outputs'; +import { bundleArazzo } from './get-test-description-from-file'; +import { CHECKS } from '../checks'; +import { createRuntimeExpressionCtx } from './context'; +import { evaluateRuntimeExpressionPayload } from '../runtime-expressions'; +import { calculateTotals, composeJsonLogs, maskSecrets } from '../cli-output'; +import { resolveRunningWorkflows } from './resolve-running-workflows'; +import { DefaultLogger } from '../../utils/logger/logger'; + +import type { + TestDescription, + AppOptions, + TestContext, + RunArgv, + Workflow, + SourceDescription, + Check, + RunWorkflowInput, +} from '../../types'; + +const logger = DefaultLogger.getInstance(); + +export async function runTestFile(argv: RunArgv, output: { harFile?: string; jsonFile?: string }) { + const { + file: filePath, + workflow, + verbose, + input, + skip, + server, + harOutput, + jsonOutput, + severity, + } = argv; + + const options = { + workflowPath: filePath, // filePath or documentPath + workflow, + skip, + verbose, + harOutput, + jsonOutput, + metadata: { ...argv }, + input, + server, + severity, + mutualTls: { + clientCert: argv?.clientCert, + clientKey: argv?.clientKey, + caCert: argv?.caCert, + }, + }; + + const bundledTestDescription = await bundleArazzo(filePath); + const descriptionCopy = JSON.parse(JSON.stringify(bundledTestDescription)); + + const { harLogs, jsonLogs, workflows, secretFields } = await runWorkflows( + bundledTestDescription, + options + ); + + if (output?.harFile && Object.keys(harLogs).length) { + const parsedHarLogs = maskSecrets(harLogs, secretFields || new Set()); + writeFileSync(output.harFile, JSON.stringify(parsedHarLogs, null, 2), 'utf-8'); + logger.log(blue(`Har logs saved in ${green(output.harFile)}`)); + logger.printNewLine(); + logger.printNewLine(); + } + + if (output?.jsonFile && jsonLogs) { + writeFileSync(output.jsonFile, JSON.stringify(jsonLogs, null, 2), 'utf-8'); + logger.log(blue(indent(`JSON logs saved in ${green(output.jsonFile)}`, 2))); + logger.printNewLine(); + logger.printNewLine(); + } + + return { workflows, parsedYaml: descriptionCopy }; +} + +async function runWorkflows(testDescription: TestDescription, options: AppOptions) { + const harLogs = options.metadata?.harOutput && createHarLog(); + const apiClient = new ApiFetcher({ + harLogs, + }); + + const ctx = await createTestContext(testDescription, options, apiClient); + + const workflowsToRun = resolveRunningWorkflows(options.workflow); + const workflowsToSkip = resolveRunningWorkflows(options.skip); + + const workflows = getWorkflowsToRun(ctx.workflows, workflowsToRun, workflowsToSkip); + + for (const workflow of workflows) { + // run dependencies workflows first + if (workflow.dependsOn?.length) { + await handleDependsOn({ workflow, ctx }); + } + + await runWorkflow({ + workflowInput: workflow.workflowId, + ctx, + }); + } + + // json logs should be composed after all workflows are run + const jsonLogs = options.jsonLogsFile ? composeJsonLogs(ctx) : undefined; + + return { ...ctx, harLogs, jsonLogs }; +} + +export async function runWorkflow({ + workflowInput, + ctx, + parentStepId, + parentWorkflowId, + fromStepId, +}: RunWorkflowInput): Promise { + const workflowStartTime = performance.now(); + const fileBaseName = basename(ctx.options.workflowPath); + const workflow = + typeof workflowInput === 'string' + ? ctx.workflows.find((w) => w.workflowId === workflowInput) + : workflowInput; + + if (!workflow) { + throw new Error(`\n ${blue('Workflow')} ${workflowInput} ${blue('not found')} \n`); + } + + const workflowName = workflow?.workflowId || parentWorkflowId; + + if (parentWorkflowId && parentStepId) { + printStepWorkflowSeparator(parentStepId, parentWorkflowId); + } else if (parentWorkflowId) { + printDependentWorkflowSeparator(parentWorkflowId); + } else { + printWorkflowSeparator(fileBaseName, workflowName); + } + + const fromStepIndex = fromStepId + ? workflow.steps.findIndex((step) => step.stepId === fromStepId) + : 0; + const workflowSteps = workflow.steps.slice(fromStepIndex); + + // clean $steps ctx before running workflow steps + ctx.$steps = {}; + + for (const step of workflowSteps) { + try { + const stepResults = await runStep({ + step, + ctx, + workflowName, + parentWorkflowId, + parentStepId, + }); + // When `end` action is used, we should not continue with the next steps + if (stepResults?.shouldEnd) { + break; + } + } catch (err: any) { + const failedCall: Check = { + name: CHECKS.UNEXPECTED_ERROR, + message: err.message, + pass: false, + severity: ctx.severity['UNEXPECTED_ERROR'], + }; + step.checks.push(failedCall); + return; + } + } + + // workflow level outputs + if (workflow.outputs && workflowName) { + if (!ctx.$outputs) { + ctx.$outputs = {}; + } + if (!ctx.$outputs[workflowName]) { + ctx.$outputs[workflowName] = {}; + } + + const runtimeExpressionContext = createRuntimeExpressionCtx({ + ctx: { + ...ctx, + $inputs: { + ...(ctx.$inputs || {}), + ...(ctx.$workflows[workflowName]?.inputs || {}), + }, + }, + workflowId: workflowName, + }); + + for (const outputKey of Object.keys(workflow.outputs)) { + try { + if (workflow.outputs) { + workflow.outputs[outputKey] = evaluateRuntimeExpressionPayload({ + payload: workflow.outputs[outputKey], + context: runtimeExpressionContext, + }); + } + ctx.$outputs[workflowName].outputs = workflow.outputs; + ctx.$workflows[workflowName].outputs = workflow.outputs; + } catch (error: any) { + throw new Error( + `Failed to resolve outputs in workflow "${workflowName}": ${error.message}` + ); + } + } + } + + workflow.time = Math.ceil(performance.now() - workflowStartTime); + logger.printNewLine(); + + return workflow; +} + +async function handleDependsOn({ workflow, ctx }: { workflow: Workflow; ctx: TestContext }) { + if (!workflow.dependsOn?.length) return; + + const dependenciesWorkflows = await Promise.all( + workflow.dependsOn.map(async (workflowId) => { + const resolvedWorkflow = getValueFromContext(workflowId, ctx); + const workflowCtx = await resolveWorkflowContext(workflowId, resolvedWorkflow, ctx); + + return runWorkflow({ + workflowInput: resolvedWorkflow, + parentWorkflowId: workflow.workflowId, + ctx: workflowCtx, + }); + }) + ); + + if (dependenciesWorkflows.some((w) => !w)) { + throw new Error('Dependent workflows failed'); + } + + const totals = calculateTotals(dependenciesWorkflows as Workflow[]); + const hasProblems = totals.steps.failed > 0; + + if (hasProblems) { + throw new Error('Dependent workflows has failed steps'); + } +} + +export async function resolveWorkflowContext( + workflowId: string | undefined, + resolvedWorkflow: Workflow, + ctx: TestContext +) { + const sourceDescriptionId = + workflowId && workflowId.startsWith('$sourceDescriptions.') && workflowId.split('.')[1]; + + const testDescription = sourceDescriptionId && ctx.$sourceDescriptions[sourceDescriptionId]; + // executing external workflow should not mutate the original context + // only outputs are transferred to the parent workflow + // creating the new ctx for the external workflow or recreate current ctx for local workflow + return testDescription + ? await createTestContext( + testDescription, + { + workflowPath: findSourceDescriptionUrl( + sourceDescriptionId, + ctx.sourceDescriptions, + ctx.options + ), + workflow: [resolvedWorkflow.workflowId], + skip: undefined, + input: ctx.options.input || undefined, + server: ctx.options.server || undefined, + severity: ctx.options.severity || undefined, + }, + ctx.apiClient + ) + : await createTestContext( + JSON.parse(JSON.stringify(ctx.testDescription)), + JSON.parse(JSON.stringify(ctx.options)), + ctx.apiClient + ); +} + +function findSourceDescriptionUrl( + sourceDescriptionId: string, + sourceDescriptions: SourceDescription[] | undefined, + options: AppOptions +) { + const sourceDescription = + sourceDescriptions && sourceDescriptions.find(({ name }) => name === sourceDescriptionId); + + if (!sourceDescription) { + return ''; + } else if (sourceDescription.type === 'openapi') { + return sourceDescription.url; + } else if (sourceDescription.type === 'arazzo') { + return resolve(dirname(options.workflowPath), sourceDescription.url); + } else { + throw new Error( + `Unknown source description type ${(sourceDescription as SourceDescription).type}` + ); + } +} diff --git a/packages/respect-core/src/modules/flow-runner/schema/index.ts b/packages/respect-core/src/modules/flow-runner/schema/index.ts new file mode 100644 index 0000000000..34703eea6e --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/schema/index.ts @@ -0,0 +1 @@ +export * from './schema-checker'; diff --git a/packages/respect-core/src/modules/flow-runner/schema/schema-checker.ts b/packages/respect-core/src/modules/flow-runner/schema/schema-checker.ts new file mode 100644 index 0000000000..28e73cdabf --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/schema/schema-checker.ts @@ -0,0 +1,180 @@ +import Ajv, { type JSONSchemaType } from '@redocly/ajv/dist/2020'; +import { diffLinesUnified } from 'jest-diff'; +import { blue, dim, red, yellow } from 'colorette'; +import { + type Check, + type DescriptionChecks, + type StepCallContext, + type TestContext, +} from '../../../types'; +import { CHECKS } from '../../checks'; +import { printErrors as printAjvErrors } from '../../../utils/ajv-errors'; +import { checkCircularRefsInSchema } from '../../../utils/check-circular-refs-in-schema'; +import { removeWriteOnlyProperties } from '../../description-parser'; +import { DefaultLogger } from '../../../utils/logger/logger'; + +const logger = DefaultLogger.getInstance(); + +const ajvStrict = new Ajv({ + schemaId: '$id', + meta: true, + allErrors: true, + strictSchema: false, + inlineRefs: false, + validateSchema: false, + discriminator: true, + allowUnionTypes: true, + validateFormats: false, + logger: false, + verbose: true, + defaultUnevaluatedProperties: false, +}); + +export function checkSchema({ + stepCallCtx, + descriptionOperation, + ctx, +}: { + stepCallCtx: StepCallContext; + descriptionOperation?: any; + ctx: TestContext; +}): Check[] { + const { $response } = stepCallCtx; + + const checks: Check[] = []; + + // if no $response, that is a common case for executing dependsOn workflow steps of workflow inside + // the step - return checks + if (!$response || !descriptionOperation) { + return checks; + } + + checkStatusCodeFromDescription({ checks, descriptionOperation, $response, ctx }); + + checkContentTypeFromDescription({ checks, descriptionOperation, $response, ctx }); + + checkSchemaFromDescription({ checks, descriptionOperation, $response, ctx }); + + return checks; +} + +function checkSchemaFromDescription({ + checks, + descriptionOperation, + $response, + ctx, +}: DescriptionChecks & { ctx: TestContext }): void { + const { body: resultBody } = $response; + const descriptionResponseByCode = + descriptionOperation?.responses[String($response?.statusCode)] || + descriptionOperation?.responses['default']; + + const schemaFromDescription = $response?.contentType + ? descriptionResponseByCode?.content?.[$response.contentType]?.schema + : undefined; + const isSchemaWithCircularRef = checkCircularRefsInSchema(schemaFromDescription); + + if (isSchemaWithCircularRef) { + logger.log(`${yellow('WARNING: schema have circular references')}`); + logger.printNewLine(); + } + + if (schemaFromDescription && !isSchemaWithCircularRef) { + try { + checks.push({ + name: CHECKS.SCHEMA_CHECK, + pass: ajvStrict.validate( + removeWriteOnlyProperties(schemaFromDescription as JSONSchemaType), + resultBody + ), + message: ajvStrict.errors + ? printAjvErrors( + removeWriteOnlyProperties(schemaFromDescription as JSONSchemaType), + resultBody, + ajvStrict.errors + ) + : '', + severity: ctx.severity['SCHEMA_CHECK'], + }); + } catch (error: any) { + checks.push({ + name: CHECKS.SCHEMA_CHECK, + pass: false, + message: `Ajv error: ${error.message as string}`, + severity: ctx.severity['SCHEMA_CHECK'], + }); + } + } +} + +function checkStatusCodeFromDescription({ + checks, + descriptionOperation, + $response, + ctx, +}: DescriptionChecks & { ctx: TestContext }): void { + const responseStatusCode = $response?.statusCode; + const responseCodesFromDescription = Object.keys(descriptionOperation?.responses || {}); + const matchesCodeFromDescription = + responseStatusCode && + responseCodesFromDescription + .map((item) => item.toString()) + .includes(responseStatusCode.toString()); + + const matchesDefaultResponse = responseCodesFromDescription.includes('default'); + + const message = matchesCodeFromDescription + ? dim(`List of valid response codes are inferred from description \n\n`) + + diffLinesUnified(responseCodesFromDescription.map(String), [`${responseStatusCode}`]) + : ''; // NOTE: we don't show any diff if response code hits default response + + const pass = matchesCodeFromDescription || matchesDefaultResponse; + + checks.push({ + name: CHECKS.STATUS_CODE_CHECK, + pass, + message, + ...(pass && { + additionalMessage: `Response code ${responseStatusCode} matches one of description codes: [${responseCodesFromDescription.join( + ', ' + )}]`, + }), + severity: ctx.severity['STATUS_CODE_CHECK'], + }); +} + +function checkContentTypeFromDescription({ + checks, + descriptionOperation, + $response, + ctx, +}: DescriptionChecks & { ctx: TestContext }): void { + const statusCode = $response?.statusCode; + const responseContentType = $response?.contentType; + const possibleContentTypesFromDescription = Object.keys( + descriptionOperation?.responses[statusCode]?.content || {} + ); + + if (!possibleContentTypesFromDescription.length) { + return; + } + + if (responseContentType && !possibleContentTypesFromDescription.includes(responseContentType)) { + checks.push({ + name: CHECKS.CONTENT_TYPE_CHECK, + pass: false, + message: `Content type ${red(responseContentType)} for ${blue( + statusCode + )} response is not described in the schema. + Expected content types: ${blue(possibleContentTypesFromDescription.join(', '))}.`, + severity: ctx.severity['CONTENT_TYPE_CHECK'], + }); + } else { + checks.push({ + name: CHECKS.CONTENT_TYPE_CHECK, + pass: true, + message: `Content type "${responseContentType}" is described in the schema.`, + severity: ctx.severity['CONTENT_TYPE_CHECK'], + }); + } +} diff --git a/packages/respect-core/src/modules/flow-runner/success-criteria/check-success-criteria.ts b/packages/respect-core/src/modules/flow-runner/success-criteria/check-success-criteria.ts new file mode 100644 index 0000000000..bee1d5af4d --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/success-criteria/check-success-criteria.ts @@ -0,0 +1,126 @@ +import { JSONPath } from 'jsonpath-plus'; +import { + validateSuccessCriteria, + isRegexpSuccessCriteria, + isJSONPathSuccessCriteria, +} from './validate-success-criteria'; +import { CHECKS } from '../../checks'; +import { evaluateRuntimeExpression } from '../../runtime-expressions'; +import { createRuntimeExpressionCtx } from '../context'; + +import type { + TestContext, + Check, + RegexpSuccessCriteria, + Step, + CriteriaObject, +} from '../../../types'; + +export function checkCriteria({ + workflowId, + step, + criteria = [], + ctx, +}: { + workflowId?: string; + step: Step; + criteria?: CriteriaObject[]; + ctx: TestContext; +}): Check[] { + validateSuccessCriteria(criteria); + + const checks: Check[] = []; + + if (!workflowId) { + checks.push({ + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: false, + message: `Undefined workflowId for step ${step.stepId}`, + severity: ctx.severity['SUCCESS_CRITERIA_CHECK'], + }); + + return checks; + } + + const criteriaContext = createRuntimeExpressionCtx({ + ctx, + workflowId, + step, + }); + + criteria.forEach((criteria: CriteriaObject) => { + const { condition } = criteria; + + try { + if (isRegexpSuccessCriteria(criteria)) { + const regexParts = condition.match(/^\/(.*)\/([gimsuy]*)$/); + + let regexPattern: string; + let flags: string; + + if (regexParts) { + regexPattern = regexParts[1]; // Extract pattern between the first and last slash + flags = regexParts[2]; // Extract flags after the last slash + } else { + regexPattern = condition; // If no slashes are present, treat the whole string as the pattern + flags = ''; // No flags in this case + } + + const { context } = criteria as RegexpSuccessCriteria; + const regex = new RegExp(regexPattern, flags); + + checks.push({ + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: regex.test(evaluateRuntimeExpression(context, criteriaContext)), + message: `Checking regex criteria: ${JSON.stringify(criteria)}`, + severity: ctx.severity['SUCCESS_CRITERIA_CHECK'], + }); + } else if (isJSONPathSuccessCriteria(criteria)) { + const { context, condition } = criteria; + const data = evaluateRuntimeExpression(context, criteriaContext); + + checks.push({ + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: evaluateJSONPAthCondition(condition, data), + message: `Checking jsonpath criteria: ${condition}`, + severity: ctx.severity['SUCCESS_CRITERIA_CHECK'], + }); + } else { + checks.push({ + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: evaluateRuntimeExpression(condition, criteriaContext), + message: `Checking simple criteria: ${JSON.stringify(criteria)}`, + severity: ctx.severity['SUCCESS_CRITERIA_CHECK'], + }); + } + } catch (e: any) { + checks.push({ + name: CHECKS.SUCCESS_CRITERIA_CHECK, + pass: false, + message: `Failed to pass ${JSON.stringify(criteria)}: ${e.message}`, + severity: ctx.severity['SUCCESS_CRITERIA_CHECK'], + }); + } + }); + + return checks; +} + +function evaluateJSONPAthCondition(condition: string, context: Record) { + // Extract JSONPath expressions from the string + const jsonpathMatches = condition.match(/\$\.[a-zA-Z0-9_]+/g) || []; + + // Replace JSONPath expressions with their values + const replacedCondition = jsonpathMatches.reduce((acc, match) => { + const jsonpathResult = JSONPath({ path: match, json: context }); + const jsonpathResultValue = jsonpathResult[0] || null; + return acc.replace(match, JSON.stringify(jsonpathResultValue)); + }, condition); + + try { + const evaluateFn = new Function(`return ${replacedCondition};`); + return !!evaluateFn(); + } catch (_error) { + return false; + } +} diff --git a/packages/respect-core/src/modules/flow-runner/success-criteria/index.ts b/packages/respect-core/src/modules/flow-runner/success-criteria/index.ts new file mode 100644 index 0000000000..1c0bd58140 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/success-criteria/index.ts @@ -0,0 +1,2 @@ +export * from './validate-success-criteria'; +export * from './check-success-criteria'; diff --git a/packages/respect-core/src/modules/flow-runner/success-criteria/validate-success-criteria.ts b/packages/respect-core/src/modules/flow-runner/success-criteria/validate-success-criteria.ts new file mode 100644 index 0000000000..1530e4c6d5 --- /dev/null +++ b/packages/respect-core/src/modules/flow-runner/success-criteria/validate-success-criteria.ts @@ -0,0 +1,78 @@ +import type { + CriteriaObject, + RegexpSuccessCriteria, + JsonPathSuccessCriteria, +} from '../../../types'; + +export function isRegexpSuccessCriteria( + criteria: CriteriaObject +): criteria is RegexpSuccessCriteria { + return (criteria as RegexpSuccessCriteria).type === 'regex'; +} + +export function isJSONPathSuccessCriteria( + criteria: CriteriaObject +): criteria is JsonPathSuccessCriteria { + const typeValue = (criteria as JsonPathSuccessCriteria)?.type; + + return ( + typeValue === 'jsonpath' || (typeof typeValue === 'object' && typeValue?.type === 'jsonpath') + ); +} + +export const ALLOWED_EXPRESSION_KEYS = [ + '$url', + '$method', + '$statusCode', + '$request', + '$response', + '$inputs', + '$outputs', + '$steps', + '$workflows', + '$sourceDescriptions', + '$components', +]; + +export function validateSuccessCriteria(successCriteria: CriteriaObject[]) { + successCriteria.forEach((criteria: CriteriaObject) => { + const { condition } = criteria; + + if (isRegexpSuccessCriteria(criteria)) { + const { context } = criteria; + const regex = /\$[a-zA-Z_]\w*/g; + const matches = context.match(regex); + + if (!matches) { + throw new Error(`"${context}" does not contain any valid context.`); + } + + const invalidKeys = matches.filter((key: string) => !ALLOWED_EXPRESSION_KEYS.includes(key)); + + if (invalidKeys.length) { + throw new Error(`Success criteria context "${context}" is not allowed.`); + } + } else if (isJSONPathSuccessCriteria(criteria)) { + if (!criteria.context) { + throw new Error(`jsonpath success criteria context is required.`); + } + + if (!criteria.condition) { + throw new Error(`jsonpath success criteria condition is required.`); + } + } else { + const regex = /\$[a-zA-Z_]\w*/g; + const matches = condition.match(regex); + + if (!matches) { + return; + } + + const invalidKeys = matches.filter((key: string) => !ALLOWED_EXPRESSION_KEYS.includes(key)); + + if (invalidKeys.length) { + throw new Error(`Success criteria condition ${condition} is not allowed.`); + } + } + }); +} diff --git a/packages/respect-core/src/modules/runtime-expressions/abnf-parser.pegjs b/packages/respect-core/src/modules/runtime-expressions/abnf-parser.pegjs new file mode 100644 index 0000000000..5159a24c35 --- /dev/null +++ b/packages/respect-core/src/modules/runtime-expressions/abnf-parser.pegjs @@ -0,0 +1,154 @@ +// Entry point for the grammar, supporting logical combinations +Start + = Expression + +// Main expression rule that allows logical operations between comparisons +Expression + = LogicalExpression + +// Define a logical expression to support '||' and '&&' between comparisons +LogicalExpression + = ComparisonExpression (_ LogicalOperator _ ComparisonExpression)* + +// Logical operators for AND, OR, and NOT +LogicalOperator + = "&&" / "||" / "!" + +// Operators and logical operators +Operator + = "==" / "!=" / "<=" / "<" / ">=" / ">" + +// Comparison expressions with basic or curly expressions +ComparisonExpression + = (CurlyExpression / BasicExpression / Value) _ (Operator _ (CurlyExpression / BasicExpression / Value))* + +// Curly brace expressions to allow nested structures +CurlyExpression + = "{" _ BasicExpression _ "}" + / "{" _ LogicalExpression _ "}" + +BasicExpression + = "$url" + / "$method" + / "$statusCode" + / "$request." Source + / "$response." Source + / "$inputs." PropertyNameChain + / OutputsExpression + / "$steps." StepsExpression + / "$workflows." WorkflowsExpression + / "$sourceDescriptions." PropertyNameChain + / "$components." PropertyNameChain + / "$components.parameters." PropertyNameChain + +// New rules for outputs with optional JSON pointer +OutputsExpression + = "$outputs." (PropertyNameChain jsonPointer? / PropertyNameWithPointer) + +// Handle steps with outputs +StepsExpression + = PropertyName "." ("outputs." PropertyNameWithPointer / PropertyNameChain) + +// Handle workflows with outputs +WorkflowsExpression + = PropertyName "." ("outputs." PropertyNameWithPointer / PropertyNameChain) + +// Property name that must be followed by a JSON pointer +PropertyNameWithPointer + = PropertyName jsonPointer + +// Chaining multiple PropertyNames with a dot +PropertyNameChain + = PropertyName (("." _ PropertyName)*) + +// Property names for chained access (limited to specific formats) +PropertyName + = Name + / Number + +// Reference sources +Source + = HeaderReference + / QueryReference + / PathReference + / BodyReference + +HeaderReference + = "header." Token + +QueryReference + = "query." Name + +PathReference + = "path." Name + +BodyReference + = "body" jsonPointer? + +// JSON pointer to refer to specific parts of the body +jsonPointer + = "#" JsonPathSegment* + +// JSON path segments can have unescaped or escaped characters +JsonPathSegment + = "/" (Unescaped / Escaped)* + +// Valid unescaped characters +Unescaped + = [\x21-\x2E\x30-\x39\x41-\x5A\x5F\x61-\x7A] + +// Escaped characters +Escaped + = "~" ("0" / "1") + +// Names and tokens as defined by your ABNF rules +// Name can start with a letter followed by letters, digits, underscores, or hyphens +Name + = [a-zA-Z][a-zA-Z0-9_-]* + +// Token with specific characters +Token + = [!#$%&'*+\-.^_`|~a-zA-Z0-9-]+ + +// Supported values in comparisons +Value + = Array / Number / String / Boolean / Null / Undefined + +// Allows empty array only; expand if needed +Array + = "[" _ "]" + +Number + = Integer ("." Digits)? + +Integer + = "0" / [1-9][0-9]* + +Digits + = [0-9]+ + +// String literals can be enclosed in single or double quotes +String + = DoubleQuotedString / SingleQuotedString + +// String literal enclosed in double quotes +DoubleQuotedString + = '"' [^"]* '"' + +// String literal enclosed in single quotes +SingleQuotedString + = "'" [^']* "'" + +// Boolean literals for true and false +Boolean + = "true" / "false" + +// Null literal +Null + = "null" + +Undefined + = "undefined" + +// Optional whitespace handling +_ = [ \t\r\n]* diff --git a/packages/respect-core/src/modules/runtime-expressions/evaluate.ts b/packages/respect-core/src/modules/runtime-expressions/evaluate.ts new file mode 100644 index 0000000000..e9b399aec4 --- /dev/null +++ b/packages/respect-core/src/modules/runtime-expressions/evaluate.ts @@ -0,0 +1,206 @@ +import { isPlainObject } from '@redocly/openapi-core/lib/utils'; +import { lintExpression } from './lint'; +import { replaceJSONPointers } from './replace-json-pointers'; +import { getFakeData, parseJson } from '../config-parser'; + +import type { RuntimeExpressionContext } from '../../types'; + +// Used when evaluating expressions in a string that can contain other text, like request bodies payload, output values, etc. +export function evaluateRuntimeExpressionPayload({ + payload, + context, + contentType, +}: { + payload: any; + context: RuntimeExpressionContext; + contentType?: string; +}): any { + if ( + contentType?.includes('application/octet-stream') || + contentType?.includes('multipart/form-data') + ) { + return parseJson(payload, context); // Return parsed file content as JSON + } + if (typeof payload === 'string') { + // Resolve string expressions + return isPureRuntimeExpression(payload) + ? evaluateRuntimeExpression(payload, context) + : evaluateExpressionsInString(payload, context); + } else if (isPlainObject(payload)) { + return Object.entries(payload).reduce((acc, [key, value]) => { + acc[key] = evaluateRuntimeExpressionPayload({ payload: value, context }); + return acc; + }, {} as Record); + } else if (Array.isArray(payload)) { + // Handle each element in an array + return payload.map((item) => evaluateRuntimeExpressionPayload({ payload: item, context })); + } else { + // Return the payload as-is if it's not a string, object, or array + return payload; + } +} + +// Evaluate runtime expressions in a given expression object. Used in SuccessCriteria conditions. +export function evaluateRuntimeExpression(expression: any, context: RuntimeExpressionContext): any { + if (typeof expression === 'string') { + return evaluateExpressionString(expression, context); + } else if (isPlainObject(expression)) { + return Object.entries(expression).reduce((acc, [key, value]) => { + acc[key] = value && evaluateRuntimeExpression(value, context); + return acc; + }, {} as Record); + } else if (Array.isArray(expression)) { + return expression.map((exp) => evaluateRuntimeExpression(exp, context)); + } else { + return expression; + } +} + +function evaluateExpressionString(expression: string, context: RuntimeExpressionContext) { + // Replace $faker expressions with fake data as it is not the part of the Runtime + // Expressions and should be evaluated separately + if (/^\$faker\.[a-zA-Z0-9._-]+(\([^\\)]*\))?$/.test(expression)) { + return getFakeData(expression.slice(1), context); + } else if (expression.includes('$faker.')) { + const fakerRegex = /\$faker\.[a-zA-Z0-9._-]+(\([^\\)]*\))?/g; + expression = expression + .replace(fakerRegex, (match) => { + return getFakeData(match.slice(1), context); + }) + .replace(/{(.*?)}/g, '$1'); + } + + if (!expression.includes('$') && !/[=<>]/.test(expression)) { + return expression; + } + + // Validate expression syntax to match ABNF Arazzo grammar + lintExpression(expression); + + const normalizedExpression = normalizeExpression(expression, context); + + // Normalize the context for evaluation by replacing hyphens with underscores in all keys + const normalizedContext = normalizeContext(context); + + try { + // Create a new Function to evaluate the expression + const evaluate = new Function( + ...Object.keys(normalizedContext), + `return ${normalizedExpression};` + ); + + // Evaluate the modified expression + return evaluate(...Object.values(normalizedContext)); + } catch (_error) { + throw new Error( + `Error in resolving runtime expression '${expression}'. \n` + + "This could be because the expression references a value from a previous failed step, or is trying to reference a variable that hasn't been set." + ); + } +} + +// Normalize the expression to replace hyphens with underscores and convert to lowercase +function normalizeSymbolsExpression(expression: string): string { + return expression.replace(/\$([a-zA-Z0-9._-]+)/g, (_match, variable) => { + // Normalize variable by replacing hyphens with underscores and converting to lowercase + const normalizedKey = variable.replace(/-/g, '_'); // Replace hyphens with underscores + + return `$${normalizedKey}`; // Return the normalized variable for evaluation + }); +} + +function normalizeExpression(expression: string, context: RuntimeExpressionContext): string { + const modifiedJsExpression = replaceJSONPointers(expression, context); + // Normalize the expression for evaluation by replacing hyphens with underscores and converting to lowercase + const normalizedSymbolsExpression = normalizeSymbolsExpression(modifiedJsExpression); + + // Remove the curly braces surrounding the expression (if any) + const cleanedJsExpression = normalizedSymbolsExpression.replace(/{(.*?)}/g, '$1'); + + // Convert numeric indices (e.g., `.0`) into square bracket notation (e.g., `[0]`) + const expressionWithBrackets = convertNumericIndices(cleanedJsExpression); + + // Check if the expression contains `.header.` and lowercase the parameter after `.header.`. + // As headers are case-insensitive, we need to normalize the header names to lowercase for evaluation. + const headerParameterNameRegex = /\.header\.([a-zA-Z0-9._-]+)/g; + const normalizedExpression = expressionWithBrackets.replace( + headerParameterNameRegex, + (_match, p1) => { + return `.header.${p1.toLowerCase()}`; + } + ); + + return normalizedExpression; +} + +// Normalize the entire context to replace hyphens with underscores in all keys +function normalizeContext(context: RuntimeExpressionContext): Record { + const normalized = {} as Record; + + for (const [key, value] of Object.entries(context)) { + // Normalize variable names to lowercase and replace hyphens with underscores + const normalizedKey = key.replace(/-/g, '_'); + + normalized[normalizedKey] = normalizeValue(value); + } + + return normalized; +} + +// Normalize values recursively, handling objects and primitives +function normalizeValue(value: any): any { + if (Array.isArray(value)) { + // If the value is an array, return it as-is without modifying + return value; + } else if (typeof value === 'object' && value !== null) { + return normalizeObject(value); + } + return value; +} + +// Normalize an object by replacing hyphens with underscores in keys +function normalizeObject(obj: Record): Record { + return Object.keys(obj).reduce((acc, key) => { + const normalizedKey = key.replace(/-/g, '_'); // Convert hyphens to underscores + acc[normalizedKey] = normalizeValue(obj[key]); + return acc; + }, {} as Record); +} + +function convertNumericIndices(expression: string): string { + // Match any dot followed by a number (e.g., .0, .1, etc.) and replace it with [0], [1], etc. + // but not modify floats. + return expression.replace(/\.(\d+)/g, (match, num, offset, str) => { + // Look at the character right before the dot + const charBeforeDot = str[offset - 1]; + // If the character before the dot is a digit, it's a float + const isFloat = /\d/.test(charBeforeDot); + return isFloat ? match : `[${num}]`; + }); +} + +// Helper function to evaluate expressions within a string +function evaluateExpressionsInString( + expression: string, + context: RuntimeExpressionContext +): string { + const regex = /\{(?:[^{}]|\{[^{}]*\})*\}|\$[^\s{}]+(?:\([^()]*\))*/g; + + return expression.replace(regex, (match) => { + const exprToEvaluate = match.trim(); + + // For dollar expressions, include the leading $ when passing to evaluateRuntimeExpression + const evaluatedValue = evaluateRuntimeExpression(exprToEvaluate, context); + + // Return evaluated value or the original match if undefined + return evaluatedValue !== undefined ? evaluatedValue : match; + }); +} + +export function isPureRuntimeExpression(expression: string): boolean { + // Regular expression to match runtime expressions + const regex = /^\$[^\s{}]+(\([^)]*\))?$|^\{[^{}]*\}$/; + + // Check if the expression matches the runtime expression format + return regex.test(expression.trim()); +} diff --git a/packages/respect-core/src/modules/runtime-expressions/index.ts b/packages/respect-core/src/modules/runtime-expressions/index.ts new file mode 100644 index 0000000000..5992e08e12 --- /dev/null +++ b/packages/respect-core/src/modules/runtime-expressions/index.ts @@ -0,0 +1 @@ +export * from './evaluate'; diff --git a/packages/respect-core/src/modules/runtime-expressions/lint.ts b/packages/respect-core/src/modules/runtime-expressions/lint.ts new file mode 100644 index 0000000000..ef5531a823 --- /dev/null +++ b/packages/respect-core/src/modules/runtime-expressions/lint.ts @@ -0,0 +1,9 @@ +const parser = require('./abnf-parser'); + +export function lintExpression(expression: string) { + try { + return parser.parse(expression); + } catch (_error) { + throw new Error(`Runtime expression is not valid: ${expression}`); + } +} diff --git a/packages/respect-core/src/modules/runtime-expressions/replace-json-pointers.ts b/packages/respect-core/src/modules/runtime-expressions/replace-json-pointers.ts new file mode 100644 index 0000000000..4f0ec5bc39 --- /dev/null +++ b/packages/respect-core/src/modules/runtime-expressions/replace-json-pointers.ts @@ -0,0 +1,65 @@ +const JsonPointer = require('json-pointer'); + +export function replaceJSONPointers(expression: string, context: any): string { + const jsonPointerReplacementRules = [ + { + pattern: /\$response\.body#\/([\w/]+)/g, + ctxFunction: (match: string, pointer: string) => { + return resolvePointer(context.$response?.body, pointer, match); + }, + }, + { + pattern: /\$request\.body#\/([\w/]+)/g, + ctxFunction: (match: string, pointer: string) => { + return resolvePointer(context.$request?.body, pointer, match); + }, + }, + { + pattern: /\$outputs\.([\w-]+)#\/([\w/]+)/g, + ctxFunction: (match: string, property: string, pointer: string) => { + return resolvePointer(context.$outputs?.[property], pointer, match); + }, + }, + { + pattern: /\$workflows\.([\w-]+)\.outputs\.([\w-]+)#\/([\w/]+)/g, + ctxFunction: (match: string, workflowId: string, property: string, pointer: string) => { + return resolvePointer( + context.$workflows?.[workflowId]?.outputs?.[property], + pointer, + match + ); + }, + }, + { + pattern: /\$steps\.([\w-]+)\.outputs\.([\w-]+)#\/([\w/]+)/g, + ctxFunction: (match: string, stepId: string, property: string, pointer: string) => { + return resolvePointer(context.$steps?.[stepId]?.outputs?.[property], pointer, match); + }, + }, + ]; + + let result = expression; + for (const { pattern, ctxFunction } of jsonPointerReplacementRules) { + result = result.replaceAll(pattern, ctxFunction); + } + + return result; +} + +function resolvePointer(sourceContext: any, pointer: string, fallbackMatch: string): string { + if (sourceContext) { + try { + const value = JsonPointer.get(sourceContext, `/${pointer}`); + if (typeof value === 'string') { + return JSON.stringify(value); // Safely quote the strings + } + if (Array.isArray(value) || typeof value === 'object') { + return JSON.stringify(value); + } + return value !== undefined ? value : fallbackMatch; + } catch (_error) { + return fallbackMatch; + } + } + return fallbackMatch; +} diff --git a/packages/respect-core/src/modules/test-config-generator/cleanup-test-description.ts b/packages/respect-core/src/modules/test-config-generator/cleanup-test-description.ts new file mode 100644 index 0000000000..2939e03677 --- /dev/null +++ b/packages/respect-core/src/modules/test-config-generator/cleanup-test-description.ts @@ -0,0 +1,30 @@ +import type { TestDescription, Workflow, Step } from '../../types'; + +// Scraps-out sensitive information from a test case +export function cleanupTestDescription(testDescription: TestDescription) { + const { workflows, arazzo, sourceDescriptions } = testDescription; + const workflowsCleaned = [] as Workflow[]; + + workflows?.map((workflow) => { + workflowsCleaned.push(cleanupWorkflow(workflow)); + }); + + return { + arazzo, + sourceDescriptions, + workflows: workflowsCleaned, + }; +} + +function cleanupWorkflow(workflow: Workflow): any { + const { workflowId, steps } = workflow; + return { + workflowId, + steps: steps?.map(cleanupStep), + }; +} + +function cleanupStep(step: Step): any { + const { stepId, parameters, successCriteria } = step; + return { stepId, successCriteria, parameters: parameters?.map(({ value: _, ...rest }) => rest) }; +} diff --git a/packages/respect-core/src/modules/test-config-generator/generate-example-value.ts b/packages/respect-core/src/modules/test-config-generator/generate-example-value.ts new file mode 100644 index 0000000000..27c44fa5ed --- /dev/null +++ b/packages/respect-core/src/modules/test-config-generator/generate-example-value.ts @@ -0,0 +1,14 @@ +import { generateTestDataFromJsonSchema } from './generate-test-data-from-json-schema'; +import { extractFirstExample } from '../description-parser'; + +import type { Parameter } from '../../types'; + +export function generateExampleValue(parameter: Parameter) { + if (parameter?.example) { + return parameter.example; + } else if (parameter?.examples) { + return extractFirstExample(parameter.examples); + } else if (parameter?.schema) { + return generateTestDataFromJsonSchema(parameter.schema); + } +} diff --git a/packages/respect-core/src/modules/test-config-generator/generate-test-config.ts b/packages/respect-core/src/modules/test-config-generator/generate-test-config.ts new file mode 100644 index 0000000000..013f120ea8 --- /dev/null +++ b/packages/respect-core/src/modules/test-config-generator/generate-test-config.ts @@ -0,0 +1,135 @@ +import * as path from 'path'; +import { sortMethods } from '../../utils/sort'; +import { bundleOpenApi } from '../description-parser'; + +import type { + OperationMethod, + TestDescription, + GenerateConfigFileArgv, + Workflow, + Step, +} from '../../types'; + +type WorkflowsFromDescriptionInput = { + descriptionPaths: any; + sourceDescriptionName: string; + options: { + extended?: boolean; + }; +}; + +function generateParametersWithSuccessCriteria( + responses: any +): [] | { successCriteria: { condition: string }[] } { + const responseCodesFromDescription = Object.keys(responses || {}); + + if (!responseCodesFromDescription.length) { + return []; + } + + const firstResponseCode = responseCodesFromDescription?.[0]; + return { successCriteria: [{ condition: `$statusCode == ${firstResponseCode}` }] }; +} + +function generateOperationId(path: string, method: OperationMethod) { + return `${method}@${path}`; +} + +function generateWorkflowsFromDescription({ + descriptionPaths, + options, + sourceDescriptionName, +}: WorkflowsFromDescriptionInput): Workflow[] { + const { extended } = options; + const workflows = [] as Workflow[]; + + for (const pathItemKey in descriptionPaths) { + for (const pathItemObjectKey of Object.keys(descriptionPaths[pathItemKey]).sort(sortMethods)) { + const keyToCheck = pathItemObjectKey.toLocaleLowerCase(); + + if ( + [ + 'get', + 'post', + 'put', + 'delete', + 'patch', + 'head', + 'options', + 'trace', + 'connect', + 'query', + ].includes(keyToCheck.toLocaleLowerCase()) + ) { + const method = keyToCheck as OperationMethod; + const pathKey = pathItemKey + .replace(/^\/|\/$/g, '') + .split('/') + .join('-'); + + const resolvedOperationId = + descriptionPaths[pathItemKey][method]?.operationId || + generateOperationId(pathItemKey, method); + + workflows.push({ + workflowId: pathKey ? `${method}-${pathKey}-workflow` : `${method}-workflow`, + steps: [ + { + stepId: pathKey ? `${method}-${pathKey}-step` : `${method}-step`, + operationId: `$sourceDescriptions.${sourceDescriptionName}.${resolvedOperationId}`, + ...(extended && + generateParametersWithSuccessCriteria( + descriptionPaths[pathItemKey][method].responses + )), + } as unknown as Step, + ], + }); + } + } + } + + return workflows; +} + +export const infoSubstitute = { + title: '[REPLACE WITH API title]', + version: '[REPLACE WITH API version]', +}; + +function resolveDescriptionNameFromPath(descriptionPath: string): string { + return path.parse(descriptionPath).name; +} + +export async function generateTestConfig({ + descriptionPath, + outputFile, + extended, +}: GenerateConfigFileArgv) { + const { paths: pathsObject, info } = (await bundleOpenApi(descriptionPath, '')) || {}; + const sourceDescriptionName = resolveDescriptionNameFromPath(descriptionPath); + const resolvedDescriptionPath = outputFile + ? path.relative(path.dirname(outputFile), path.resolve(descriptionPath)) + : descriptionPath; + + const testDescription: TestDescription = { + arazzo: '1.0.1', + info: { + title: info?.title || infoSubstitute.title, + version: info?.version || infoSubstitute.version, + }, + sourceDescriptions: [ + { + name: sourceDescriptionName, + type: 'openapi', + url: resolvedDescriptionPath, + }, + ], + workflows: generateWorkflowsFromDescription({ + descriptionPaths: pathsObject, + options: { extended }, + sourceDescriptionName, + }), + }; + + return JSON.parse(JSON.stringify(testDescription, null, 2)); +} diff --git a/packages/respect-core/src/modules/test-config-generator/generate-test-data-from-json-schema.ts b/packages/respect-core/src/modules/test-config-generator/generate-test-data-from-json-schema.ts new file mode 100644 index 0000000000..06efa41802 --- /dev/null +++ b/packages/respect-core/src/modules/test-config-generator/generate-test-data-from-json-schema.ts @@ -0,0 +1,15 @@ +import * as Sampler from 'openapi-sampler'; +import { bgRed } from 'colorette'; +import { DefaultLogger } from '../../utils/logger/logger'; + +const logger = DefaultLogger.getInstance(); + +export function generateTestDataFromJsonSchema(schema: any) { + if (!schema) return; + try { + return Sampler.sample(schema, { skipReadOnly: true, skipNonRequired: false, quiet: true }); + } catch (e) { + logger.error(bgRed(` Error while generating test data from JSON Schema `) + '\n' + e); + return; + } +} diff --git a/packages/respect-core/src/modules/test-config-generator/index.ts b/packages/respect-core/src/modules/test-config-generator/index.ts new file mode 100644 index 0000000000..fbc8abe4f6 --- /dev/null +++ b/packages/respect-core/src/modules/test-config-generator/index.ts @@ -0,0 +1,4 @@ +export * from './generate-test-config'; +export * from './cleanup-test-description'; +export * from './generate-test-data-from-json-schema'; +export * from './generate-example-value'; diff --git a/packages/respect-core/src/types.ts b/packages/respect-core/src/types.ts new file mode 100644 index 0000000000..517121399c --- /dev/null +++ b/packages/respect-core/src/types.ts @@ -0,0 +1,264 @@ +import type { FromSchema } from 'json-schema-to-ts'; +import type { + parameter, + operationMethod, + sourceDescriptionSchema, + infoObject, + requestBody, + replacement, + criteriaObject, + step, + workflow, + reusableObject, + onSuccessObject, + onFailureObject, + extendedOperation, +} from './arazzo-schema'; +import type { Faker } from './modules/faker'; +import type { OperationDetails } from './modules/description-parser'; +import type { RuleSeverity } from '@redocly/openapi-core/lib/config/types'; +import type { ApiFetcher } from './utils/api-fetcher'; + +export type OperationMethod = FromSchema; +export type ResponseContext = { + statusCode: number; + body: any; + header: Record; + query?: Record; + path?: Record; + contentType?: string; +} & Record; +export type SourceDescription = FromSchema; +type ArazzoParameter = FromSchema; +export type InfoObject = FromSchema; +export type RequestBody = FromSchema; +export type Replacement = FromSchema; +export type CriteriaObject = FromSchema; +export type ReusableObject = FromSchema; +export type OnSuccessObject = FromSchema; +export type OnFailureObject = FromSchema; +export type ExtendedOperation = FromSchema; +type ArazzoStep = FromSchema; +type ArazzoWorkflow = FromSchema & { + steps: Step[]; +}; + +export type AdditionalParameterProperties = { + style?: string; + target?: string; + required?: boolean; + schema?: Record; + example?: unknown; + examples?: Record | unknown; +}; +type ExtendedParameter = T & AdditionalParameterProperties; +export type Parameter = ExtendedParameter; +export type ResolvedParameter = Parameter & { + in?: 'header' | 'query' | 'path' | 'cookie'; + name: string; +}; +export type VerboseLog = { + method: OperationMethod; + path: string; + host: string; + body?: any; + headerParams?: Record; + statusCode?: number; + responseTime?: number; +}; +type AdditionalStepProps = { + verboseLog?: VerboseLog; + response: ResponseContext; + checks: Check[]; +}; +export type Step = ArazzoStep & AdditionalStepProps; +export type Workflow = Omit & { steps: Step[]; time?: number }; +export type RunArgv = { + file: string; + testDescription?: TestDescription; + workflow?: string[]; + skip?: string[]; + verbose?: boolean; + harOutput?: string; + jsonOutput?: string; + input?: string | string[]; + server?: string | string[]; + severity?: string | string[]; + clientCert?: NonNullable['clientCert']; + clientKey?: NonNullable['clientKey']; + caCert?: NonNullable['caCert']; +}; + +export interface RequestContext { + body: any; + header: Record; + path?: Record; + method?: string; + url?: string; + query?: Record; +} + +export type ParsedParameters = { + queryParams: Record; + pathParams: Record; + headerParams: Record; +}; +export type AppOptions = { + workflowPath: string; + workflow?: string | string[]; + skip?: string | string[]; + verbose?: boolean; + harLogsFile?: string; + jsonLogsFile?: string; + metadata?: Record; + input?: string | string[]; + server?: string | string[]; + severity?: string | string[]; + mutualTls?: Partial; +}; +export type RegexpSuccessCriteria = { + condition: string; + context: string; + type: 'regex'; +}; +export type JsonPathVerison = 'draft-goessner-dispatch-jsonpath-00'; + +export type JsonPathSuccessCriterionObject = { + type: 'jsonpath'; + version: JsonPathVerison; +}; + +export type JsonPathSuccessCriteria = { + condition: string; + context: string; + type: 'jsonpath' | JsonPathSuccessCriterionObject; +}; +export type Ref = { + $ref: string; +}; +export type PublicWorkflow = { + outputs?: Record; + inputs?: Record; + steps: Record; +}; +export type PublicStep = { + outputs?: Record; +}; + +export type PublicWorkflowStep = { + outputs?: Record; + request?: RequestContext; + response?: ResponseContext; +}; + +export interface InputSchema { + type: string; + properties?: { + [key: string]: any; + }; + format?: string; +} + +export type StepInnerContext = { + $response: ResponseContext | undefined; + $request: RequestContext | undefined; + $outputs: Record; +}; +export type WorkflowInnerContext = { + $steps: Record; + $outputs: Record; +}; +export type RuntimeExpressionContext = { + $workflows: Record; + $sourceDescriptions: Record; + $faker: Faker; + $steps: Record; + $response?: Partial; + $request?: Partial; + $outputs?: Record; + $inputs?: Record; + $components?: Record; + $url?: string; + $method?: string; + $statusCode?: number; +}; + +export type RunWorkflowInput = { + workflowInput: Workflow | string; + ctx: TestContext; + parentStepId?: string; + parentWorkflowId?: string; + fromStepId?: string; +}; + +export type TestContext = RuntimeExpressionContext & { + arazzo: string; + info: InfoObject & Record; + sourceDescriptions?: SourceDescription[]; + workflows: Workflow[]; + options: AppOptions; + testDescription: TestDescription; + harLogs: any; + components?: Record; + secretFields?: Set; + severity: Record; + mtlsCerts?: { + clientCert?: string; + clientKey?: string; + caCert?: string; + }; + apiClient: ApiFetcher; +}; +export type TestDescription = Partial< + Pick< + TestContext & WorkflowInnerContext & StepInnerContext, + 'workflows' | 'arazzo' | 'info' | 'sourceDescriptions' | '$outputs' | 'components' + > +>; +export type CheckStepInfo = { + stepId: string; + workflowId?: string; + operationId?: string; + operationPath?: string; +}; +export type Check = { + severity: RuleSeverity; + stepInfo?: CheckStepInfo; + pass: boolean; + name: string; + message?: string; + additionalMessage?: string; +}; + +export type GenerateConfigFileArgv = { + descriptionPath: string; + outputFile?: string; + extended?: boolean; +}; + +export interface ResultsOfTests { + passed: number; + failed: number; + total: number; + warnings: number; + skipped: number; +} + +export type CalculatedResults = { + workflows: ResultsOfTests; + steps: ResultsOfTests; + checks: ResultsOfTests; +}; +export type StepCallContext = { + $response?: ResponseContext; + $request?: RequestContext; + $inputs?: Record; +}; +type StepWithChecks = PublicStep & { checks?: Check[] }; +type WorkflowWithChecks = PublicWorkflow & { steps: Record; time?: number }; +export type JsonLogs = Record; +export type DescriptionChecks = { + checks: Check[]; + descriptionOperation: OperationDetails; + $response: ResponseContext; +}; diff --git a/packages/respect-core/src/utils/__tests__/__snapshots__/ajv-errors.test.ts.snap b/packages/respect-core/src/utils/__tests__/__snapshots__/ajv-errors.test.ts.snap new file mode 100644 index 0000000000..7d2317023b --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/__snapshots__/ajv-errors.test.ts.snap @@ -0,0 +1,38 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`printErrors should display multiple Ajv errors 1`] = ` +" +UNEVALUATEDPROPERTIES must NOT have unevaluated properties: "date".: "date". + + 1 | [ +> 2 | { + | ^ +> 3 | "date": "2023-09-11", + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +> 4 | "timeOpen": "09:00", + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +> 5 | "timeClose": "18:00" + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +> 6 | }, + | ^^^^ 👈🏽 unevaluatedProperties must NOT have unevaluated properties: "date".: "date". + 7 | { + 8 | "date": "2023-09-12", + 9 | "timeOpen": "09:00", + +UNEVALUATEDPROPERTIES must NOT have unevaluated properties: "date".: "date". + + 5 | "timeClose": "18:00" + 6 | }, +> 7 | { + | ^ +> 8 | "date": "2023-09-12", + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +> 9 | "timeOpen": "09:00", + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +> 10 | "timeClose": "18:00" + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +> 11 | } + | ^^^^ 👈🏽 unevaluatedProperties must NOT have unevaluated properties: "date".: "date". + 12 | ] +" +`; diff --git a/packages/respect-core/src/utils/__tests__/ajv-errors.test.ts b/packages/respect-core/src/utils/__tests__/ajv-errors.test.ts new file mode 100644 index 0000000000..4fb9c261c9 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/ajv-errors.test.ts @@ -0,0 +1,59 @@ +import type { JSONSchemaType } from '@redocly/ajv/dist/2020'; + +import { printErrors } from '../ajv-errors'; +import { cleanColors } from '../../utils/clean-colors'; + +describe('printErrors', () => { + it('should display multiple Ajv errors', () => { + const schema = { + description: 'List of museum operating hours for consecutive days.', + type: 'array', + items: { + description: 'Daily operating hours for the museum.', + type: 'object', + properties: { timeOpen: [Object], timeClose: [Object] }, + required: ['date', 'timeOpen', 'timeClose'], + }, + } as unknown as JSONSchemaType; + const errors = [ + { + instancePath: '/0', + schemaPath: '#/items/unevaluatedProperties', + keyword: 'unevaluatedProperties', + params: { unevaluatedProperty: 'date' }, + message: 'must NOT have unevaluated properties: "date".', + schema: undefined, + parentSchema: { + description: 'Daily operating hours for the museum.', + type: 'object', + properties: [Object], + required: [Array], + }, + data: { date: '2023-09-11', timeOpen: '09:00', timeClose: '18:00' }, + }, + { + instancePath: '/1', + schemaPath: '#/items/unevaluatedProperties', + keyword: 'unevaluatedProperties', + params: { unevaluatedProperty: 'date' }, + message: 'must NOT have unevaluated properties: "date".', + schema: undefined, + parentSchema: { + description: 'Daily operating hours for the museum.', + type: 'object', + properties: [Object], + required: [Array], + }, + data: { date: '2023-09-12', timeOpen: '09:00', timeClose: '18:00' }, + }, + ]; + const data = [ + { date: '2023-09-11', timeOpen: '09:00', timeClose: '18:00' }, + { date: '2023-09-12', timeOpen: '09:00', timeClose: '18:00' }, + ]; + + const result = printErrors(schema, data, errors); + + expect(cleanColors(result)).toMatchSnapshot(); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/api-fetcher.test.ts b/packages/respect-core/src/utils/__tests__/api-fetcher.test.ts new file mode 100644 index 0000000000..00197428b6 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/api-fetcher.test.ts @@ -0,0 +1,159 @@ +import { normalizeHeaders, isJsonContentType, isXmlContentType, ApiFetcher } from '../api-fetcher'; + +describe('normalizeHeaders', () => { + it('should return empty object if no headers', () => { + const result = normalizeHeaders(undefined); + expect(result).toEqual({}); + }); + + it('should return empty object if empty headers', () => { + const result = normalizeHeaders({}); + expect(result).toEqual({}); + }); + + it('should return normalized headers', () => { + const result = normalizeHeaders({ + 'Content-Type': 'application/json', + 'x-api-key': '123', + }); + expect(result).toEqual({ + 'content-type': 'application/json', + 'x-api-key': '123', + }); + }); +}); + +describe('isJsonContentType', () => { + it('should return true if json mime type', () => { + const result = isJsonContentType('application/json'); + expect(result).toEqual(true); + }); + + it('should return true if json mime type with suffix', () => { + const result = isJsonContentType('application/vnd.api+json'); + expect(result).toEqual(true); + }); + + it('should return false if not json mime type', () => { + const result = isJsonContentType('application/xml'); + expect(result).toEqual(false); + }); +}); + +describe('isXmlContentType', () => { + it('should return true if xml mime type', () => { + const result = isXmlContentType('application/xml'); + expect(result).toEqual(true); + }); + + it('should return true if xml mime type with suffix', () => { + const result = isXmlContentType('application/vnd.api+xml'); + expect(result).toEqual(true); + }); + + it('should return false if not xml mime type', () => { + const result = isXmlContentType('application/json'); + expect(result).toEqual(false); + }); +}); + +describe('ApiFetcher', () => { + describe('initVerboseLogs', () => { + it('should init verboseLogs', () => { + const apiFetcher = new ApiFetcher({}); + apiFetcher.initVerboseLogs({ + host: 'localhost', + path: '/pets', + method: 'get', + body: { name: 'test' }, + headerParams: { 'x-api-key': '123' }, + }); + expect(apiFetcher.verboseLogs).toEqual({ + host: 'localhost', + path: '/pets', + method: 'get', + body: '{"name":"test"}', + headerParams: { 'x-api-key': '123' }, + }); + }); + }); + + describe('getVerboseLogs', () => { + it('should get verboseLogs', () => { + const apiFetcher = new ApiFetcher({}); + apiFetcher.verboseLogs = { + host: 'localhost', + path: '/pets', + method: 'get', + body: '{"name":"test"}', + headerParams: { 'x-api-key': '123' }, + }; + expect(apiFetcher.getVerboseLogs()).toEqual({ + host: 'localhost', + path: '/pets', + method: 'get', + body: '{"name":"test"}', + headerParams: { 'x-api-key': '123' }, + }); + }); + }); + + describe('getVerboseResponseLogs', () => { + it('should get verboseResponseLogs', () => { + const apiFetcher = new ApiFetcher({}); + apiFetcher.verboseResponseLogs = { + host: 'localhost', + path: '/pets', + method: 'get', + body: '{"name":"test"}', + headerParams: { 'x-api-key': '123' }, + }; + expect(apiFetcher.getVerboseResponseLogs()).toEqual({ + host: 'localhost', + path: '/pets', + method: 'get', + body: '{"name":"test"}', + headerParams: { 'x-api-key': '123' }, + }); + }); + }); + + describe('initVerboseResponseLogs', () => { + it('should init verboseResponseLogs', () => { + const apiFetcher = new ApiFetcher({}); + apiFetcher.initVerboseResponseLogs({ + host: 'localhost', + path: '/pets', + method: 'get', + body: { name: 'test' }, + headerParams: { 'x-api-key': '123' }, + statusCode: 200, + }); + expect(apiFetcher.verboseResponseLogs).toEqual({ + host: 'localhost', + path: '/pets', + method: 'get', + body: { name: 'test' }, + headerParams: { 'x-api-key': '123' }, + statusCode: 200, + }); + }); + }); + + describe('fetchResults', () => { + it('should throw an error if no serverUrl', async () => { + const apiFetcher = new ApiFetcher({}); + const ctx = {} as any; + const requestData = { + serverUrl: undefined, + path: '/pets', + method: 'get', + parameters: [], + requestBody: {}, + }; + await expect(apiFetcher.fetchResult(ctx, requestData)).rejects.toThrowError( + 'No server url provided' + ); + }); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/check-circular-refs-in-schema.test.ts b/packages/respect-core/src/utils/__tests__/check-circular-refs-in-schema.test.ts new file mode 100644 index 0000000000..bd1b9f203f --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/check-circular-refs-in-schema.test.ts @@ -0,0 +1,31 @@ +import { checkCircularRefsInSchema } from '../check-circular-refs-in-schema'; + +describe('checkCircularRefsInSchema', () => { + it('should return false if schema is not circular', () => { + const schema = { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + }; + + expect(checkCircularRefsInSchema(schema)).toBe(false); + }); + + it('should return true if schema is circular', () => { + const schema = { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + }; + + schema.properties.name = schema; + + expect(checkCircularRefsInSchema(schema)).toBe(true); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/clean-colors.test.ts b/packages/respect-core/src/utils/__tests__/clean-colors.test.ts new file mode 100644 index 0000000000..e04f37f06c --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/clean-colors.test.ts @@ -0,0 +1,25 @@ +import { cleanColors } from '../clean-colors'; + +describe('cleanColors', () => { + it('should remove ANSI color codes from the input string', () => { + const input = '\x1b[31mThis is red text\x1b[0m and this is normal text'; + const expectedOutput = 'This is red text and this is normal text'; + expect(cleanColors(input)).toBe(expectedOutput); + }); + + it('should return the same string if there are no ANSI color codes', () => { + const input = 'This is a normal text'; + expect(cleanColors(input)).toBe(input); + }); + + it('should handle empty strings', () => { + const input = ''; + expect(cleanColors(input)).toBe(input); + }); + + it('should handle strings with only ANSI color codes', () => { + const input = '\x1b[31m\x1b[0m'; + const expectedOutput = ''; + expect(cleanColors(input)).toBe(expectedOutput); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/cli-outputs.test.ts b/packages/respect-core/src/utils/__tests__/cli-outputs.test.ts new file mode 100644 index 0000000000..13c4954270 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/cli-outputs.test.ts @@ -0,0 +1,146 @@ +import type { Totals } from '@redocly/openapi-core'; +import type { VerboseLog } from '../../types'; + +// eslint-disable-next-line import-x/order +import { DefaultLogger } from '../../utils/logger/logger'; + +const logger = DefaultLogger.getInstance(); + +import { + printWorkflowSeparatorLine, + printWorkflowSeparator, + printStepSeparatorLine, + printConfigLintTotals, + printDependentWorkflowSeparator, + printStepWorkflowSeparator, + indent, + removeExtraIndentation, + printStepDetails, +} from '../cli-outputs'; + +describe('cliOutputs', () => { + describe('removeExtraIndentation', () => { + it('should remove extra indentation', () => { + const message = ' This is a message'; + const trimmedMessage = removeExtraIndentation(message); + expect(trimmedMessage).toBe('This is a message'); + }); + + it('should return an empty string', () => { + const message = undefined; + const trimmedMessage = removeExtraIndentation(message); + expect(trimmedMessage).toBe(''); + }); + }); + + describe('indent', () => { + it('should indent a string', () => { + const indentedString = indent('string', 2); + // eslint-disable-next-line no-irregular-whitespace + expect(indentedString).toMatchInlineSnapshot(`"  string"`); + }); + }); + + describe('printWorkflowSeparator', () => { + it('should print a separator', () => { + const mockLogger = jest.spyOn(logger, 'log').mockImplementation(); + printWorkflowSeparator('file', 'workflow'); + expect(mockLogger).toHaveBeenCalledWith(expect.stringMatching('Running workflow')); + mockLogger.mockRestore(); + }); + + it('should print a separator', () => { + const mockLogger = jest.spyOn(logger, 'log').mockImplementation(); + printWorkflowSeparator('file', undefined); + expect(mockLogger).toHaveBeenCalledWith(expect.stringMatching('Running workflow')); + mockLogger.mockRestore(); + }); + }); + + describe('printDependentWorkflowSeparator', () => { + it('should print a separator', () => { + const mockLogger = jest.spyOn(logger, 'log').mockImplementation(); + printDependentWorkflowSeparator('parentWorkflowId'); + expect(mockLogger).toHaveBeenCalledWith(expect.stringMatching('Running required')); + mockLogger.mockRestore(); + }); + }); + + describe('printStepWorkflowSeparator', () => { + it('should print a separator', () => { + const mockLogger = jest.spyOn(logger, 'log').mockImplementation(); + printStepWorkflowSeparator('parentStepId', 'parentWorkflowId'); + expect(mockLogger).toHaveBeenCalledWith(expect.stringMatching('Running step')); + mockLogger.mockRestore(); + }); + }); + + describe('printWorkflowSeparatorLine', () => { + it('should print a separator line', () => { + const mockLogger = jest.spyOn(logger, 'printSeparator').mockImplementation(); + printWorkflowSeparatorLine(); + expect(mockLogger).toHaveBeenCalledWith(expect.stringMatching('\u2500')); + mockLogger.mockRestore(); + }); + }); + + describe('printStepSeparatorLine', () => { + it('should print a separator line', () => { + const mockLogger = jest.spyOn(logger, 'printNewLine').mockImplementation(); + printStepSeparatorLine(); + expect(mockLogger).toHaveBeenCalled(); + mockLogger.mockRestore(); + }); + }); + + describe('printConfigLintTotals', () => { + it('should print the number of errors', () => { + const mockLogger = jest.spyOn(logger, 'error').mockImplementation(); + printConfigLintTotals({ errors: 1, warnings: 0, ignored: 0 } as Totals); + expect(mockLogger).toHaveBeenCalledWith( + expect.stringMatching('❌ Your config has 1 one error.') + ); + mockLogger.mockRestore(); + }); + + it('should print the number of warnings', () => { + const mockLogger = jest.spyOn(logger, 'error').mockImplementation(); + printConfigLintTotals({ errors: 0, warnings: 1, ignored: 0 } as Totals); + expect(mockLogger).toHaveBeenCalledWith( + expect.stringMatching('⚠️ Your config has 1 one warning.') + ); + mockLogger.mockRestore(); + }); + }); + + describe('printStepDetails', () => { + it('should print step details', () => { + const mockLogger = jest.spyOn(logger, 'log').mockImplementation(); + const verboseLogs = { + method: 'GET', + path: 'path', + host: 'host', + } as VerboseLog; + const verboseResponseLogs = { + method: 'GET', + path: 'path', + host: 'host', + } as VerboseLog; + printStepDetails({ + testNameToDisplay: 'testNameToDisplay', + checks: [ + { + name: 'CheckName', + pass: true, + message: 'message', + severity: 'error', + }, + ], + verboseLogs, + verboseResponseLogs, + }); + expect(mockLogger).toHaveBeenCalledWith(expect.stringMatching('testNameToDisplay')); + mockLogger.mockRestore(); + }); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/env.test.ts b/packages/respect-core/src/utils/__tests__/env.test.ts new file mode 100644 index 0000000000..12971d593e --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/env.test.ts @@ -0,0 +1,7 @@ +import { isBrowser } from '../env'; + +describe('env', () => { + it('should return non browser env', () => { + expect(isBrowser()).toBe(false); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/file.test.ts b/packages/respect-core/src/utils/__tests__/file.test.ts new file mode 100644 index 0000000000..c7a03d5517 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/file.test.ts @@ -0,0 +1,39 @@ +import { isTestFile } from '../file'; + +describe('isTestFile', () => { + const validDocument = { + arazzo: '1.0.1', + }; + + const notValidDocument = { + openapi: '1.0.0', + }; + + it('should return true for .yml', () => { + expect(isTestFile('test.yml', validDocument)).toBe(true); + }); + + it('should return true for .yaml', () => { + expect(isTestFile('test.yaml', validDocument)).toBe(true); + }); + + it('should return true for .YML', () => { + expect(isTestFile('test.YML', validDocument)).toBe(true); + }); + + it('should return true for test.YAML', () => { + expect(isTestFile('test.YAML', validDocument)).toBe(true); + }); + + it('should return false for test.json', () => { + expect(isTestFile('test.json', validDocument)).toBe(true); + }); + + it('should return false for .js', () => { + expect(isTestFile('test.js', validDocument)).toBe(false); + }); + + it('should return false for not valid arazzo description', () => { + expect(isTestFile('test.yaml', notValidDocument)).toBe(false); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/get-nested-value.test.ts b/packages/respect-core/src/utils/__tests__/get-nested-value.test.ts new file mode 100644 index 0000000000..9e49049976 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/get-nested-value.test.ts @@ -0,0 +1,47 @@ +import { getNestedValue } from '../get-nested-value'; + +describe('getNestedValue', () => { + it('should return the value of a nested property', () => { + const obj = { + a: { + b: { + c: 1, + }, + }, + }; + expect(getNestedValue(obj, ['a', 'b', 'c'])).toBe(1); + }); + + it('should return undefined if a nested property does not exist', () => { + const obj = { + a: { + b: { + c: 1, + }, + }, + }; + expect(getNestedValue(obj, ['a', 'b', 'd'])).toBeUndefined(); + }); + + it('should return the object itself if the path is empty', () => { + const obj = { + a: { + b: { + c: 1, + }, + }, + }; + expect(getNestedValue(obj, [])).toBe(obj); + }); + + it('should handle null and undefined values', () => { + const obj = { + a: { + b: null, + c: undefined, + }, + }; + expect(getNestedValue(obj, ['a', 'b'])).toBeNull(); + expect(getNestedValue(obj, ['a', 'c'])).toBeUndefined(); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/get-reunite-url.test.ts b/packages/respect-core/src/utils/__tests__/get-reunite-url.test.ts new file mode 100644 index 0000000000..cf38eb73cc --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/get-reunite-url.test.ts @@ -0,0 +1,17 @@ +import { getReuniteUrl } from '../get-reunite-url'; + +describe('getReuniteUrl', () => { + it('should return US Reunite url when no residency provided', () => { + expect(getReuniteUrl()).toBe('https://app.cloud.redocly.com/api'); + }); + + it('should return EU Reunite url when residency is set to eu', () => { + expect(getReuniteUrl('eu')).toBe('https://app.cloud.eu.redocly.com/api'); + }); + + it('should return residency url when url provided', () => { + expect(getReuniteUrl('http://someenvironment.redocly.com')).toBe( + 'http://someenvironment.redocly.com/api' + ); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/har-logs/create-har-log.test.ts b/packages/respect-core/src/utils/__tests__/har-logs/create-har-log.test.ts new file mode 100644 index 0000000000..cf93841ec1 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/har-logs/create-har-log.test.ts @@ -0,0 +1,33 @@ +import { createHarLog } from '../../har-logs'; + +describe('createHarLog', () => { + it('should create a har log', () => { + const mockDate = new Date('2024-01-01T00:00:00.000Z'); + jest.spyOn(global, 'Date').mockImplementation(() => mockDate as any); + + const harLog = createHarLog(); + expect(harLog).toEqual({ + log: { + version: '1.2', + creator: { + name: '@redocly/respect-core', + version: expect.any(String), + }, + pages: [ + { + startedDateTime: '2024-01-01T00:00:00.000Z', + id: 'page_1', + title: 'Page', + pageTimings: { + onContentLoad: -1, + onLoad: -1, + }, + }, + ], + entries: [], + }, + }); + + jest.restoreAllMocks(); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/har-logs/helpers/add-headers.test.ts b/packages/respect-core/src/utils/__tests__/har-logs/helpers/add-headers.test.ts new file mode 100644 index 0000000000..7ec713e304 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/har-logs/helpers/add-headers.test.ts @@ -0,0 +1,23 @@ +import { addHeaders } from '../../../har-logs/helpers/add-headers'; + +describe('addHeaders', () => { + it('should add headers to an existing Headers object', () => { + const oldHeaders = new Headers({ 'Content-Type': 'application/json' }); + const newHeaders = { Authorization: 'Bearer 1234567890' }; + const result = addHeaders(oldHeaders, newHeaders); + expect(result.get('Authorization')).toBe('Bearer 1234567890'); + }); + + it('should add headers to an existing plain object', () => { + const oldHeaders = { 'Content-Type': 'application/json' }; + const newHeaders = { Authorization: 'Bearer 1234567890' }; + const result = addHeaders(oldHeaders, newHeaders); + expect(result.Authorization).toBe('Bearer 1234567890'); + }); + + it('should return new headers if old headers are not provided', () => { + const newHeaders = { Authorization: 'Bearer 1234567890' }; + const result = addHeaders(null, newHeaders); + expect(result.Authorization).toBe('Bearer 1234567890'); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-headers.test.ts b/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-headers.test.ts new file mode 100644 index 0000000000..c384be440a --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-headers.test.ts @@ -0,0 +1,32 @@ +import { buildHeaders } from '../../../har-logs/helpers/build-headers'; + +describe('buildHeaders', () => { + it('should build headers from an array', () => { + const headers = buildHeaders(['Accept', '*/*', 'User-Agent', 'undici']); + expect(headers).toEqual([ + { name: 'Accept', value: '*/*' }, + { name: 'User-Agent', value: 'undici' }, + ]); + }); + + it('should build headers from an object', () => { + const headers = buildHeaders({ Accept: '*/*', 'User-Agent': 'undici' }); + expect(headers).toEqual([ + { name: 'Accept', value: '*/*' }, + { name: 'User-Agent', value: 'undici' }, + ]); + }); + + it('should build headers from a Map', () => { + const headers = buildHeaders( + new Map([ + ['Accept', '*/*'], + ['User-Agent', 'undici'], + ]) + ); + expect(headers).toEqual([ + { name: 'Accept', value: '*/*' }, + { name: 'User-Agent', value: 'undici' }, + ]); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-params.test.ts b/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-params.test.ts new file mode 100644 index 0000000000..bd6ea24b71 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-params.test.ts @@ -0,0 +1,29 @@ +import { buildParams } from '../../../har-logs/helpers/build-params'; + +describe('buildParams', () => { + it('should parse url-encoded form data into HAR params format', () => { + const urlEncodedData = 'name=test&colors=red&colors=blue'; + + const result = buildParams(urlEncodedData); + + expect(result).toEqual([ + { name: 'name', value: 'test' }, + { name: 'colors', value: 'red' }, + { name: 'colors', value: 'blue' }, + ]); + }); + + it('should handle single values', () => { + const urlEncodedData = 'single=value'; + + const result = buildParams(urlEncodedData); + + expect(result).toEqual([{ name: 'single', value: 'value' }]); + }); + + it('should handle empty string', () => { + const result = buildParams(''); + + expect(result).toEqual([]); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-request-cookies.test.ts b/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-request-cookies.test.ts new file mode 100644 index 0000000000..5c78c71538 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-request-cookies.test.ts @@ -0,0 +1,13 @@ +import { buildRequestCookies } from '../../../har-logs/helpers/build-request-cookies'; + +describe('buildRequestCookies', () => { + it('should build cookies from an array', () => { + const cookies = buildRequestCookies({ cookie: ['sessionId=1234567890'] }); + expect(cookies).toEqual([{ name: 'sessionId', value: '1234567890' }]); + }); + + it('should build cookies from an object', () => { + const cookies = buildRequestCookies({ cookie: 'sessionId=1234567890' }); + expect(cookies).toEqual([{ name: 'sessionId', value: '1234567890' }]); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-response-cookies.test.ts b/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-response-cookies.test.ts new file mode 100644 index 0000000000..0ac6ed7261 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/har-logs/helpers/build-response-cookies.test.ts @@ -0,0 +1,80 @@ +import { buildResponseCookies } from '../../../har-logs/helpers/build-response-cookies'; + +describe('buildResponseCookies', () => { + it('should build response cookies', () => { + const headers = { + 'set-cookie': [ + 'name=value; Path=/; Expires=Fri, 25 Dec 2024 12:00:00 GMT; HttpOnly; Secure', + 'name2=value2; Path=/; Expires=Fri, 25 Dec 2024 12:00:00 GMT; HttpOnly; Secure', + ], + }; + const cookies = buildResponseCookies(headers); + expect(cookies).toEqual([ + { + name: 'name', + value: 'value', + path: '/', + expires: '2024-12-25T12:00:00.000Z', + httpOnly: true, + secure: true, + }, + { + name: 'name2', + value: 'value2', + path: '/', + expires: '2024-12-25T12:00:00.000Z', + httpOnly: true, + secure: true, + }, + ]); + }); + + it('should handle empty headers', () => { + const headers = {}; + const cookies = buildResponseCookies(headers); + expect(cookies).toEqual([]); + }); + + it('should handle headers with no set-cookie', () => { + const headers = { 'content-type': 'application/json' }; + const cookies = buildResponseCookies(headers); + expect(cookies).toEqual([]); + }); + + it('should handle headers with invalid set-cookie', () => { + const headers = { 'set-cookie': ['invalid-cookie'] }; + const cookies = buildResponseCookies(headers); + expect(cookies).toEqual([ + { httpOnly: false, name: '', secure: false, value: 'invalid-cookie' }, + ]); + }); + + it('should set domain if present', () => { + const headers = { + 'set-cookie': [ + 'name=value; Domain=example.com; Path=/; Expires=Fri, 25 Dec 2024 12:00:00 GMT; HttpOnly; Secure', + ], + }; + const cookies = buildResponseCookies(headers); + expect(cookies[0].domain).toEqual('example.com'); + }); + + it('should handle Headers instance with valid cookie format', () => { + const headers = new Headers(); + headers.append( + 'set-cookie', + 'name=value; Path=/; Expires=Fri, 25 Dec 2024 12:00:00 GMT; HttpOnly; Secure' + ); + const cookies = buildResponseCookies(headers); + expect(cookies).toEqual([ + { + name: 'name', + value: 'value', + path: '/', + expires: '2024-12-25T12:00:00.000Z', + httpOnly: true, + secure: true, + }, + ]); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/har-logs/helpers/get-agent.test.ts b/packages/respect-core/src/utils/__tests__/har-logs/helpers/get-agent.test.ts new file mode 100644 index 0000000000..3599502a96 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/har-logs/helpers/get-agent.test.ts @@ -0,0 +1,94 @@ +import { Dispatcher } from 'undici'; +import * as http from 'http'; + +import { getAgent } from '../../../har-logs/helpers/get-agent'; + +describe('getAgent', () => { + it('should return an agent', () => { + const input = 'https://example.com'; + const agent = getAgent(input, { har: {} }); + expect(agent).toBeDefined(); + }); + + it('should return an agent with a dispatcher', () => { + const input = 'https://example.com'; + const agent = getAgent(input, { har: {}, dispatcher: new Dispatcher() }); + expect(agent).toBeDefined(); + }); + + it('should return an agent with a custom agent', () => { + const input = 'https://example.com'; + const agent = getAgent(input, { har: {}, agent: new http.Agent() }); + expect(agent).toBeDefined(); + }); + + it('should return an agent with a custom agent function', () => { + const input = 'https://example.com'; + const agent = getAgent(input, { har: {}, agent: () => new http.Agent() }); + expect(agent).toBeDefined(); + }); + + it('should return an agent with a custom dispatcher', () => { + const input = 'https://example.com'; + const agent = getAgent(input, { har: {}, dispatcher: new Dispatcher() }); + expect(agent).toBeDefined(); + }); + + it('should return an agent with a custom agent and dispatcher', () => { + const input = 'https://example.com'; + const agent = getAgent(input, { + har: {}, + agent: new http.Agent(), + dispatcher: new Dispatcher(), + }); + expect(agent).toBeDefined(); + }); + + it('should throw error for invalid URL', () => { + const input = 'not-a-valid-url'; + expect(() => getAgent(input, { har: {} })).toThrow('Invalid URL'); + }); + + it('should handle http protocol', () => { + const input = 'http://example.com'; + const agent = getAgent(input, { har: {} }); + expect(agent).toBeDefined(); + expect(agent.protocol).toBe('http:'); + }); + + it('should handle https protocol', () => { + const input = 'https://example.com'; + const agent = getAgent(input, { har: {} }); + expect(agent).toBeDefined(); + expect(agent.protocol).toBe('https:'); + }); + + it('should handle agent function with protocol', () => { + const input = 'https://example.com'; + const agentFn = () => new http.Agent(); + const agent = getAgent(input, { har: {}, agent: agentFn }); + expect(agent).toBeDefined(); + }); + + it('should handle undefined agent and dispatcher', () => { + const input = 'https://example.com'; + const agent = getAgent(input, { har: {} }); + expect(agent).toBeDefined(); + expect(agent).toBeInstanceOf(http.Agent); + }); + + it('should handle dispatcher function', () => { + const input = 'https://example.com'; + const dispatcherFn = () => new Dispatcher(); + const agent = getAgent(input, { har: {}, dispatcher: dispatcherFn }); + expect(agent).toBeDefined(); + expect(agent).toBeInstanceOf(Function); + }); + + it('should handle agent instance without protocol check', () => { + const input = 'https://example.com'; + const customAgent = new http.Agent(); + const agent = getAgent(input, { har: {}, agent: customAgent }); + expect(agent).toBe(customAgent); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/har-logs/helpers/get-duration.test.ts b/packages/respect-core/src/utils/__tests__/har-logs/helpers/get-duration.test.ts new file mode 100644 index 0000000000..abf605d91f --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/har-logs/helpers/get-duration.test.ts @@ -0,0 +1,13 @@ +import { getDuration } from '../../../har-logs/helpers/get-duration'; + +describe('getDuration', () => { + it('should return the duration in milliseconds', () => { + const result = getDuration([1, 0], [2, 0]); + expect(result).toBe(1000); + }); + + it('should return -1000 if duration is negative', () => { + const result = getDuration([2, 0], [1, 0]); + expect(result).toBe(-1000); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/har-logs/helpers/get-input-url.test.ts b/packages/respect-core/src/utils/__tests__/har-logs/helpers/get-input-url.test.ts new file mode 100644 index 0000000000..7899d83bb6 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/har-logs/helpers/get-input-url.test.ts @@ -0,0 +1,13 @@ +import { getInputUrl } from '../../../har-logs/helpers/get-input-url'; + +describe('getInputUrl', () => { + it('should return a URL object', () => { + const url = getInputUrl('https://example.com'); + expect(url).toBeInstanceOf(URL); + }); + + it('should return a URL object from an object', () => { + const url = getInputUrl({ url: 'https://example.com' }); + expect(url).toBeInstanceOf(URL); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/har-logs/helpers/handle-request.test.ts b/packages/respect-core/src/utils/__tests__/har-logs/helpers/handle-request.test.ts new file mode 100644 index 0000000000..8fc4477645 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/har-logs/helpers/handle-request.test.ts @@ -0,0 +1,190 @@ +import { EventEmitter } from 'events'; + +import type { Dispatcher } from 'undici'; + +import { handleRequest } from '../../../har-logs/helpers/handle-request'; + +describe('handleRequest', () => { + it('should handle undici request', () => { + const input = { + method: 'GET', + url: 'https://api.example.com/test', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ test: 'test' }), + } as unknown as Request; + + const harLog = new Map(); + const isUndici = true; + const handler: Dispatcher.DispatchHandlers = { + onConnect: jest.fn(), + onHeaders: jest.fn(), + onData: jest.fn(), + onComplete: jest.fn(), + }; + + handleRequest({ input, handler, harLog, isUndici }); + }); + + it('should handle node request', () => { + const input = { + method: 'GET', + url: 'https://api.example.com/test', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ test: 'test' }), + } as unknown as Request; + + const harLog = new Map(); + const isUndici = false; + const handler = new EventEmitter(); + handler.on = jest.fn(); + handler.emit = jest.fn(); + + handleRequest({ input, handler, harLog, isUndici }); + }); + + it('should handle undici request with response data', () => { + const input = { + method: 'POST', + url: 'https://api.example.com/test', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ test: 'test' }), + } as unknown as Request; + + const harLog = new Map(); + const isUndici = true; + const handler: Dispatcher.DispatchHandlers = { + onConnect: jest.fn(), + onHeaders: jest.fn(), + onData: jest.fn(), + onComplete: jest.fn(), + }; + + // Mock response data + const responseHeaders = { + 'content-type': ['application/json'], + 'content-length': ['123'], + }; + const responseData = Buffer.from(JSON.stringify({ response: 'data' })); + + const wrappedHandler = handleRequest({ + input, + handler, + harLog, + isUndici, + }) as Dispatcher.DispatchHandlers; + + // Simulate response events using the wrapped handler + wrappedHandler?.onHeaders?.(200, responseHeaders as any, () => {}, {} as any); + wrappedHandler?.onData?.(responseData); + wrappedHandler?.onComplete?.({} as any); + }); + + it('should handle node request with response data', () => { + const input = { + method: 'POST', + url: 'https://api.example.com/test', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ test: 'test' }), + } as unknown as Request; + + const harLog = new Map(); + const isUndici = false; + const handler = new EventEmitter(); + const onSpy = jest.fn(); + handler.on = onSpy; + handler.emit = jest.fn(); + + handleRequest({ input, handler, harLog, isUndici }); + + // Verify event listeners are registered + expect(onSpy).toHaveBeenCalledWith('response', expect.any(Function)); + expect(onSpy).toHaveBeenCalledWith('data', expect.any(Function)); + expect(onSpy).toHaveBeenCalledWith('end', expect.any(Function)); + + // Get registered event handlers + const [[, responseHandler], [, dataHandler], [, endHandler]] = onSpy.mock.calls; + + // Simulate response events + const response = { + statusCode: 200, + headers: { + 'content-type': 'application/json', + 'content-length': '123', + }, + }; + responseHandler(response); + + // Simulate data event + const chunk = Buffer.from(JSON.stringify({ response: 'data' })); + dataHandler(chunk); + + // Simulate end event + endHandler(); + }); + + it('should handle errors in undici request', () => { + const input = { + method: 'GET', + url: 'https://api.example.com/test', + headers: {}, + body: null, + } as unknown as Request; + + const harLog = new Map(); + const isUndici = true; + const handler: Dispatcher.DispatchHandlers = { + onConnect: jest.fn(), + onHeaders: jest.fn(), + onData: jest.fn(), + onComplete: jest.fn(), + onError: jest.fn(), + }; + + handleRequest({ input, handler, harLog, isUndici }); + + // Simulate error + const error = new Error('Network error'); + handler?.onError?.(error); + }); + + it('should handle errors in node request', () => { + const input = { + method: 'GET', + url: 'https://api.example.com/test', + headers: {}, + body: null, + } as unknown as Request; + + const harLog = new Map(); + const isUndici = false; + const handler = new EventEmitter(); + const onSpy = jest.fn(); + handler.on = onSpy; + handler.emit = jest.fn(); + + handleRequest({ input, handler, harLog, isUndici }); + + // Find the error handler from all registered handlers + const errorHandler = onSpy.mock.calls.find(([event]) => event === 'error')?.[1]; + expect(errorHandler).toBeDefined(); + + // Simulate error event + const error = new Error('Network error'); + errorHandler(error); + + // Verify HAR log was updated with error + const [harEntry] = Array.from(harLog.values()); + expect(harEntry.log.entries[0].response).toMatchObject({ + status: 0, + statusText: 'Network error', + }); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/is-empty.test.ts b/packages/respect-core/src/utils/__tests__/is-empty.test.ts new file mode 100644 index 0000000000..d0123e9c04 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/is-empty.test.ts @@ -0,0 +1,46 @@ +import { isEmpty } from '../is-empty'; + +describe('isEmpty', () => { + describe('when value is null or undefined', () => { + it('should return true', () => { + expect(isEmpty(null)).toEqual(true); + expect(isEmpty(undefined)).toEqual(true); + }); + }); + + describe('when value is an object', () => { + it('should return true if the object is empty', () => { + expect(isEmpty({})).toEqual(true); + }); + + it('should return false if the object is not empty', () => { + expect(isEmpty({ a: 1 })).toEqual(false); + }); + }); + + describe('when value is a string', () => { + it('should return true if the string is empty', () => { + expect(isEmpty('')).toEqual(true); + }); + + it('should return false if the string is not empty', () => { + expect(isEmpty('a')).toEqual(false); + }); + }); + + describe('when value is an array', () => { + it('should return true if the array is empty', () => { + expect(isEmpty([])).toEqual(true); + }); + + it('should return false if the array is not empty', () => { + expect(isEmpty([1])).toEqual(false); + }); + }); + + describe('when value is a number', () => { + it('should return false', () => { + expect(isEmpty(1)).toEqual(false); + }); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/is-json.test.ts b/packages/respect-core/src/utils/__tests__/is-json.test.ts new file mode 100644 index 0000000000..9f5c12c8de --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/is-json.test.ts @@ -0,0 +1,11 @@ +import { isJSON } from '../is-json'; + +describe('isJSON', () => { + it('should return true if the string is a valid JSON', () => { + expect(isJSON('{"key": "value"}')).toBe(true); + }); + + it('should return false if the string is not a valid JSON', () => { + expect(isJSON('not a json')).toBe(false); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/is-url.test.ts b/packages/respect-core/src/utils/__tests__/is-url.test.ts new file mode 100644 index 0000000000..9ee56adf5d --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/is-url.test.ts @@ -0,0 +1,18 @@ +import { isURL } from '../is-url'; + +describe('isURL', () => { + it('should return true for valid urls', () => { + expect(isURL('http://example.com')).toBe(true); + expect(isURL('https://example.com')).toBe(true); + expect(isURL('http://example.com/path')).toBe(true); + expect(isURL('https://example.com/path')).toBe(true); + expect(isURL('http://example.com/path?query=1')).toBe(true); + expect(isURL('https://example.com/path?query=1')).toBe(true); + }); + + it('should return false for invalid urls', () => { + expect(isURL('example.com')).toBe(false); + expect(isURL('example.com/path')).toBe(false); + expect(isURL('example.com/path?query=1')).toBe(false); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/mtls/create-mtls-client.test.ts b/packages/respect-core/src/utils/__tests__/mtls/create-mtls-client.test.ts new file mode 100644 index 0000000000..2e0efd016d --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/mtls/create-mtls-client.test.ts @@ -0,0 +1,17 @@ +import { createMtlsClient } from '../../mtls/create-mtls-client'; + +describe('createMtlsClient', () => { + it('should create a client with the correct certificates', () => { + const client = createMtlsClient('https://example.com', { + clientCert: '-----BEGIN CERTIFICATE-----\nclientCert\n-----END CERTIFICATE-----', + clientKey: '-----BEGIN PRIVATE KEY-----\nclientKey\n-----END PRIVATE KEY-----', + caCert: '-----BEGIN CERTIFICATE-----\ncaCert\n-----END CERTIFICATE-----', + }); + expect(client).toBeDefined(); + }); + + it('should not create a client if the certificates are not provided', () => { + const client = createMtlsClient('https://example.com'); + expect(client).toBeUndefined(); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/mtls/resolve-mtls-certificates.test.ts b/packages/respect-core/src/utils/__tests__/mtls/resolve-mtls-certificates.test.ts new file mode 100644 index 0000000000..ca8079182d --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/mtls/resolve-mtls-certificates.test.ts @@ -0,0 +1,124 @@ +import * as fs from 'node:fs'; + +import { resolveMtlsCertificates } from '../../mtls/resolve-mtls-certificates'; + +// jest.mock must come before any variable declarations +jest.mock('node:fs', () => { + const actual = jest.requireActual('node:fs'); + const mockReadFileSync = jest.fn(); + const mockAccessSync = jest.fn(); + + return { + __esModule: true, + default: { + ...actual, + accessSync: mockAccessSync, + readFileSync: mockReadFileSync, + }, + constants: { + ...actual.constants, + R_OK: 4, + }, + accessSync: mockAccessSync, + readFileSync: mockReadFileSync, + }; +}); + +const mockReadFileSync = fs.readFileSync as jest.Mock; +const mockAccessSync = fs.accessSync as jest.Mock; + +describe('resolveMtlsCertificates', () => { + beforeEach(() => { + jest.clearAllMocks(); + // Default successful mock implementations + mockAccessSync.mockImplementation(() => undefined); // successful access returns undefined + mockReadFileSync.mockImplementation((path: string) => { + switch (path) { + case 'clientCert.pem': + return '-----BEGIN CERTIFICATE-----\nclientCert\n-----END CERTIFICATE-----'; + case 'clientKey.pem': + return '-----BEGIN PRIVATE KEY-----\nclientKey\n-----END PRIVATE KEY-----'; + case 'caCert.pem': + return '-----BEGIN CERTIFICATE-----\ncaCert\n-----END CERTIFICATE-----'; + default: + throw new Error('File not found'); + } + }); + }); + + it('should resolve certificates', () => { + const certs = resolveMtlsCertificates({ + clientCert: + '-----BEGIN CERTIFICATE-----\nMIICWDCCAd+gAwIBAgIJAP8L\n-----END CERTIFICATE-----', + clientKey: '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0B\n-----END PRIVATE KEY-----', + caCert: '-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAK7P\n-----END CERTIFICATE-----', + }); + + expect(certs).toEqual({ + clientCert: + '-----BEGIN CERTIFICATE-----\nMIICWDCCAd+gAwIBAgIJAP8L\n-----END CERTIFICATE-----', + clientKey: '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0B\n-----END PRIVATE KEY-----', + caCert: '-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAK7P\n-----END CERTIFICATE-----', + }); + }); + + it('should resolve certificates from file', () => { + const certs = resolveMtlsCertificates({ + clientCert: 'clientCert.pem', + clientKey: 'clientKey.pem', + caCert: 'caCert.pem', + }); + + expect(certs).toEqual({ + clientCert: '-----BEGIN CERTIFICATE-----\nclientCert\n-----END CERTIFICATE-----', + clientKey: '-----BEGIN PRIVATE KEY-----\nclientKey\n-----END PRIVATE KEY-----', + caCert: '-----BEGIN CERTIFICATE-----\ncaCert\n-----END CERTIFICATE-----', + }); + }); + + it('should throw error if file not found', () => { + // Override default mock for this specific test + mockAccessSync.mockImplementation(() => { + throw new Error('File not found'); + }); + + expect(() => + resolveMtlsCertificates({ + clientCert: 'clientCert.pem', + clientKey: 'clientKey.pem', + caCert: 'caCert.pem', + }) + ).toThrow('Failed to read certificate: File not found'); + }); + + it('should resolve certificate content in case some cert is not provided', () => { + const certs = resolveMtlsCertificates({ + clientCert: 'clientCert.pem', + clientKey: 'clientKey.pem', + }); + + expect(certs).toEqual({ + clientCert: '-----BEGIN CERTIFICATE-----\nclientCert\n-----END CERTIFICATE-----', + clientKey: '-----BEGIN PRIVATE KEY-----\nclientKey\n-----END PRIVATE KEY-----', + caCert: undefined, + }); + }); + + it('should return undefined if cert is not provided', () => { + const certs = resolveMtlsCertificates({}); + + expect(certs).toEqual({ + clientCert: undefined, + clientKey: undefined, + caCert: undefined, + }); + }); + + it('should throw error if certificate is not valid', () => { + expect(() => + resolveMtlsCertificates({ + clientCert: '-----BEGIN CERTIFICATE--22323-----END CERTIFICATE-----', + }) + ).toThrow('Invalid certificate format'); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/sort.test.ts b/packages/respect-core/src/utils/__tests__/sort.test.ts new file mode 100644 index 0000000000..6463378f72 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/sort.test.ts @@ -0,0 +1,13 @@ +import { sortMethods } from '../sort'; + +describe('sortMethods', () => { + it('should sort methods', () => { + expect(['post', 'put', 'get', 'patch', 'delete'].sort(sortMethods)).toEqual([ + 'post', + 'put', + 'get', + 'patch', + 'delete', + ]); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/time.test.ts b/packages/respect-core/src/utils/__tests__/time.test.ts new file mode 100644 index 0000000000..f8a259b853 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/time.test.ts @@ -0,0 +1,19 @@ +import { getExecutionTime } from '../time'; + +describe('getExecutionTime', () => { + it('returns a string with ms', () => { + expect(getExecutionTime(0)).toMatch(/ms/); + }); + + it('returns a string with ms', () => { + expect(getExecutionTime(40)).toMatch(/ms/); + }); + + it('returns a string with ms with different NODE_ENV', () => { + // set NODE_ENV to test + process.env.NODE_ENV = 'development'; + expect(getExecutionTime(10)).toMatch(/ms/); + // reset NODE_ENV + process.env.NODE_ENV = 'development'; + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/url.test.ts b/packages/respect-core/src/utils/__tests__/url.test.ts new file mode 100644 index 0000000000..cd2e3a4f60 --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/url.test.ts @@ -0,0 +1,19 @@ +import { combineUrl } from '../url'; + +describe('combineUrl', () => { + it('should combine host and path', () => { + expect(combineUrl('https://example.com', '/path')).toBe('https://example.com/path'); + }); + + it('should combine host and path without double slashes', () => { + expect(combineUrl('https://example.com/', '/path')).toBe('https://example.com/path'); + }); + + it('should combine host and path without double slashes', () => { + expect(combineUrl('https://example.com', 'path')).toBe('https://example.com/path'); + }); + + it('should combine host and path without double slashes', () => { + expect(combineUrl('https://example.com/', 'path')).toBe('https://example.com/path'); + }); +}); diff --git a/packages/respect-core/src/utils/__tests__/yaml.test.ts b/packages/respect-core/src/utils/__tests__/yaml.test.ts new file mode 100644 index 0000000000..4cd809d56c --- /dev/null +++ b/packages/respect-core/src/utils/__tests__/yaml.test.ts @@ -0,0 +1,68 @@ +import { readFileSync } from 'fs'; + +import { parseYaml, stringifyYaml, readYaml } from '../yaml'; + +jest.mock('fs'); + +describe('yaml', () => { + describe('parseYaml', () => { + it('should parse yaml', () => { + const yaml = ` + name: test + description: test + `; + expect(parseYaml(yaml)).toEqual({ + name: 'test', + description: 'test', + }); + }); + }); + + describe('stringifyYaml', () => { + it('should stringify yaml', () => { + const yaml = ` + name: test + description: test + `; + expect(stringifyYaml(parseYaml(yaml))).toEqual(`name: test\ndescription: test\n`); + }); + + it('should stringify yaml with options', () => { + const yaml = ` + name: test + description: test + `; + expect(stringifyYaml(parseYaml(yaml), { noRefs: true })).toEqual( + `name: test\ndescription: test\n` + ); + }); + }); + + describe('readYaml', () => { + const yaml = ` + name: test + description: test + `; + const path = './test.yaml'; + + it('should read yaml', () => { + (readFileSync as jest.Mock).mockReturnValue(yaml); + + expect(readYaml(path)).toEqual({ + name: 'test', + description: 'test', + }); + expect(readFileSync).toHaveBeenCalledWith(path, 'utf-8'); + }); + + it('should read yaml with options', () => { + (readFileSync as jest.Mock).mockReturnValue(yaml); + + expect(readYaml(path)).toEqual({ + name: 'test', + description: 'test', + }); + expect(readFileSync).toHaveBeenCalledWith(path, 'utf-8'); + }); + }); +}); diff --git a/packages/respect-core/src/utils/ajv-errors.ts b/packages/respect-core/src/utils/ajv-errors.ts new file mode 100644 index 0000000000..2d89272a54 --- /dev/null +++ b/packages/respect-core/src/utils/ajv-errors.ts @@ -0,0 +1,26 @@ +import betterAjvErrors from 'better-ajv-errors'; +import { RESET_ESCAPE_CODE } from './cli-outputs'; + +import type { JSONSchemaType } from '@redocly/ajv/dist/2020'; + +export function printErrors(schema: JSONSchemaType, data: any, errors: any[]) { + const updatedErrors = errors.map((error: any) => { + if (error.keyword === 'unevaluatedProperties' || error.keyword === 'additionalProperties') { + const failedProp = error.params.unevaluatedProperty || error.params.additionalProperty; + // Add a custom message with the unevaluated or the additional property information + return { + ...error, + message: `${error.message}: "${failedProp}".`, + }; + } + return error; + }); + + // Use betterAjvErrors with the modified errors + const output = betterAjvErrors(schema, data, updatedErrors, { + format: 'cli', + indent: 2, + }); + + return `${RESET_ESCAPE_CODE}\n${output}${RESET_ESCAPE_CODE}\n`; +} diff --git a/packages/respect-core/src/utils/api-fetcher.ts b/packages/respect-core/src/utils/api-fetcher.ts new file mode 100644 index 0000000000..e5ae955015 --- /dev/null +++ b/packages/respect-core/src/utils/api-fetcher.ts @@ -0,0 +1,298 @@ +import { fetch } from 'undici'; +import { bgRed, inverse } from 'colorette'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore this works but some types are not working +import concat from 'concat-stream'; +import { + type OperationMethod, + type VerboseLog, + type TestContext, + type ResponseContext, +} from '../types'; +import { withHar } from '../utils/har-logs'; +import { isEmpty } from './is-empty'; +import { resolvePath } from '../modules/config-parser'; +import { getVerboseLogs, maskSecrets } from '../modules/cli-output'; +import { getResponseSchema } from '../modules/description-parser'; +import { collectSecretFields } from '../modules/flow-runner'; +import { createMtlsClient } from './mtls/create-mtls-client'; +import { DefaultLogger } from './logger/logger'; + +import type { RequestData } from '../modules/flow-runner'; + +const logger = DefaultLogger.getInstance(); + +const MAX_FETCH_TIMEOUT = 20000; + +interface IFetcher { + verboseLogs?: VerboseLog; + verboseResponseLogs?: VerboseLog; + harLogs?: any; + fetch?: typeof fetch; +} + +export function normalizeHeaders(headers: Record | undefined) { + if (!headers) { + return {}; + } + + const headersToReturn: Record = {}; + + for (const key in headers) { + const lowerCaseKey = key.toLowerCase(); + + // Only add the first occurrence of any header (case-insensitive) + if (!headersToReturn[lowerCaseKey]) { + headersToReturn[lowerCaseKey] = headers[key]; + } + } + + return headersToReturn; +} + +export function isJsonContentType(contentType: string) { + return /^application\/([a-z.-]+\+)?json$/.test(contentType); +} + +export function isXmlContentType(contentType: string) { + return /^application\/([a-z.-]+\+)?xml$/.test(contentType); +} + +export function trimTrailingSlash(str: string): string { + return str.endsWith('/') ? str.slice(0, -1) : str; +} + +export class ApiFetcher implements IFetcher { + verboseLogs?: VerboseLog; + verboseResponseLogs?: VerboseLog; + harLogs?: any; + fetch?: typeof fetch; + + constructor(params: IFetcher) { + this.harLogs = params.harLogs; + this.fetch = params.fetch || fetch; + } + + initVerboseLogs = ({ headerParams, host, path, method, body }: VerboseLog) => { + this.verboseLogs = getVerboseLogs({ + headerParams, + host, + path, + method, + body: JSON.stringify(body), + }); + }; + + getVerboseLogs = () => { + return this.verboseLogs; + }; + + initVerboseResponseLogs = ({ + headerParams, + host, + path, + method, + body, + statusCode, + responseTime, + }: VerboseLog) => { + this.verboseResponseLogs = getVerboseLogs({ + headerParams, + host, + path, + method, + body, + statusCode, + responseTime, + }); + }; + + getVerboseResponseLogs = () => { + return this.verboseResponseLogs; + }; + + fetchResult = async ( + ctx: TestContext, + requestData: RequestData + ): Promise => { + const { serverUrl, path, method, parameters, requestBody, openapiOperation } = requestData; + if (!serverUrl?.url) { + logger.error(bgRed(` No server url provided `)); + throw new Error('No server url provided'); + } + + const headers: Record = {}; + const searchParams = new URLSearchParams(); + const pathParams: Record = {}; + const cookies: Record = {}; + + for (const param of parameters) { + switch (param.in) { + case 'header': + headers[param.name.toLowerCase()] = String(param.value); + break; + case 'query': + searchParams.set(param.name, String(param.value)); + break; + case 'path': + pathParams[param.name] = String(param.value); + break; + case 'cookie': + cookies[param.name] = String(param.value); + break; + } + } + + if (typeof requestBody === 'object' && !headers['content-type']) { + headers['content-type'] = 'application/json'; + } + + let resolvedPath = resolvePath(path, pathParams) || ''; + const pathWithSearchParams = `${resolvedPath}${ + searchParams.toString() ? '?' + searchParams.toString() : '' + }`; + const pathToFetch = `${trimTrailingSlash(serverUrl.url)}${pathWithSearchParams}`; + + if (pathToFetch.startsWith('/')) { + logger.error( + bgRed(` Wrong url: ${inverse(pathToFetch)} `) + + ` Did you forget to provide a correct "serverUrl"?\n` + ); + } + + const contentType = headers['content-type'] || ''; + if (requestBody && !contentType) { + logger.error( + bgRed(` Incorrect request `) + + ` Please provide a correct "content-type" header or specify the "content-type" field in the test case itself. \n` + ); + } + + let encodedBody; + if (contentType === 'application/x-www-form-urlencoded') { + encodedBody = new URLSearchParams(requestBody).toString(); + } else if (isJsonContentType(contentType || '')) { + encodedBody = JSON.stringify(requestBody); + } else if (isXmlContentType(contentType)) { + encodedBody = requestBody; + } else if (contentType.includes('multipart/form-data')) { + // Get the form data buffer + encodedBody = await new Promise((resolve, reject) => { + requestBody.pipe( + concat((data: Buffer) => { + resolve(data); + }) + ); + + requestBody.on('error', reject); + }); + + // Ensure the content-type header includes the boundary + headers['content-type'] = `multipart/form-data; boundary=${requestBody._boundary}`; + } else if (contentType === 'application/octet-stream') { + // Convert ReadStream to Blob for undici fetch + encodedBody = await new Promise((resolve, reject) => { + const chunks: Uint8Array[] = []; + requestBody.on('data', (chunk: Buffer) => { + chunks.push(new Uint8Array(chunk.buffer)); + }); + requestBody.on('end', () => resolve(Buffer.concat(chunks))); + requestBody.on('error', reject); + }); + + const fileName = requestBody.path.split('/').pop(); + headers['content-disposition'] = `attachment; filename=${fileName}`; + } else { + encodedBody = requestBody; + } + + // Mask the secrets in the header params and the body + const maskedHeaderParams = maskSecrets(headers, ctx.secretFields || new Set()); + const maskedBody = + isJsonContentType(contentType) && encodedBody + ? maskSecrets(JSON.parse(encodedBody), ctx.secretFields || new Set()) + : encodedBody; + const maskedPathParams = maskSecrets(pathWithSearchParams, ctx.secretFields || new Set()); + + // Start of the verbose logs + this.initVerboseLogs({ + headerParams: maskedHeaderParams, + host: serverUrl.url, + method: (method || 'get').toUpperCase() as OperationMethod, + path: maskedPathParams || '', + body: maskedBody, + }); + + const wrappedFetch = this.harLogs ? withHar(this.fetch, { har: this.harLogs }) : fetch; + // Resolve pathToFetch with pathParams for the second time in order + // to handle described servers->variables in the OpenAPI spec. + // E.G.: + // servers: + // - url: 'https://api-sandbox.redocly.com/organizations/{organizationId}' + // TODO: remove/update after the support of the described servers->variables in the Arazzo spec. + resolvedPath = resolvePath(pathToFetch, pathParams) || ''; + if (!resolvedPath) { + throw new Error('Path to fetch is undefined'); + } + + const startTime = performance.now(); + + const result = await wrappedFetch(resolvedPath, { + method: (method || 'get').toUpperCase() as OperationMethod, + headers, + ...(!isEmpty(requestBody) && { + body: encodedBody, + }), + redirect: 'follow', + signal: AbortSignal.timeout(MAX_FETCH_TIMEOUT), + // Required for application/octet-stream content type requests + ...(headers['content-type'] === 'application/octet-stream' && { + duplex: 'half', + }), + dispatcher: ctx.mtlsCerts ? createMtlsClient(resolvedPath, ctx.mtlsCerts) : undefined, + }); + const responseTime = Math.ceil(performance.now() - startTime); + const res = await result.text(); + + const [responseContentType] = result.headers.get('content-type')?.split(';') || [ + 'application/json', + ]; + const transformedBody = res + ? isJsonContentType(responseContentType) + ? JSON.parse(res) + : res + : {}; + const responseSchema = getResponseSchema({ + statusCode: result.status, + contentType: responseContentType, + descriptionResponses: openapiOperation?.responses, + }); + + collectSecretFields(ctx, responseSchema, transformedBody); + + const maskedResponseBody = isJsonContentType(responseContentType) + ? maskSecrets(transformedBody, ctx.secretFields || new Set()) + : transformedBody; + + this.initVerboseResponseLogs({ + body: isJsonContentType(responseContentType) + ? JSON.stringify(maskedResponseBody) + : maskedResponseBody, + method: (method || 'get') as OperationMethod, + host: serverUrl.url, + path: pathWithSearchParams || '', + statusCode: result.status, + responseTime, + }); + + return { + body: transformedBody, + statusCode: result.status, + time: responseTime, + header: Object.fromEntries(result.headers?.entries() || []), + contentType: responseContentType, + query: Object.fromEntries(searchParams.entries()), + path: pathParams, + }; + }; +} diff --git a/packages/respect-core/src/utils/check-circular-refs-in-schema.ts b/packages/respect-core/src/utils/check-circular-refs-in-schema.ts new file mode 100644 index 0000000000..47d0da75c0 --- /dev/null +++ b/packages/respect-core/src/utils/check-circular-refs-in-schema.ts @@ -0,0 +1,9 @@ +export function checkCircularRefsInSchema(schema: any): boolean { + try { + JSON.stringify(schema); + } catch { + return true; + } + + return false; +} diff --git a/packages/respect-core/src/utils/clean-colors.ts b/packages/respect-core/src/utils/clean-colors.ts new file mode 100644 index 0000000000..c70e95faae --- /dev/null +++ b/packages/respect-core/src/utils/clean-colors.ts @@ -0,0 +1,4 @@ +export function cleanColors(input: string): string { + // eslint-disable-next-line no-control-regex + return input.replace(/\x1b\[\d+m/g, ''); +} diff --git a/packages/respect-core/src/utils/cli-outputs.ts b/packages/respect-core/src/utils/cli-outputs.ts new file mode 100644 index 0000000000..18d6071a39 --- /dev/null +++ b/packages/respect-core/src/utils/cli-outputs.ts @@ -0,0 +1,97 @@ +import { pluralize } from 'jest-matcher-utils'; +import { red, yellow, bold, blue } from 'colorette'; +import { type Totals } from '@redocly/openapi-core'; +import { type Check, type VerboseLog } from '../types'; +import { displayChecks } from '../modules/cli-output'; +import { DefaultLogger } from './logger/logger'; + +const logger = DefaultLogger.getInstance(); + +export const RESET_ESCAPE_CODE = '\x1B[0m'; + +export function removeExtraIndentation(message: string | undefined): string { + if (!message) { + return ''; + } + // Split the message into individual lines + const lines = message.split('\n'); + + // Trim leading whitespace from each line + const trimmedLines = lines.map((line) => line.trimStart()); + + // Join the trimmed lines back into a single string + return trimmedLines.join('\n'); +} + +export function indent(str: string, level: number) { + return str + .split('\n') + .map((line) => '\xa0'.repeat(level) + line) + .join('\n'); +} + +export function printWorkflowSeparatorLine() { + logger.printSeparator('\u2500'); + logger.printNewLine(); + logger.printNewLine(); +} + +export function printWorkflowSeparator(fileName: string, workflowName: string | undefined) { + printWorkflowSeparatorLine(); + logger.log(` ${bold('Running workflow')} ${blue(`${fileName} / ${workflowName}`)}`); + logger.printNewLine(); +} + +export function printDependentWorkflowSeparator(parentWorkflowId: string) { + logger.printNewLine(); + logger.log( + ` ${bold('Running required')} workflows for ${blue(parentWorkflowId)} ${RESET_ESCAPE_CODE}\n` + ); +} + +export function printStepWorkflowSeparator(parentStepId: string, parentWorkflowId: string) { + logger.printNewLine(); + logger.log(` ${bold('Running step')} ${blue(parentStepId)} workflow ${blue(parentWorkflowId)}`); + logger.printNewLine(); +} + +export function printStepSeparatorLine() { + logger.printNewLine(); +} + +export function printConfigLintTotals(totals: Totals): void { + if (totals.errors > 0) { + logger.error( + red( + `❌ Your config has ${totals.errors} ${pluralize( + 'error', + totals.errors + )}.${RESET_ESCAPE_CODE}` + ) + ); + } else if (totals.warnings > 0) { + logger.error( + yellow( + `⚠️ Your config has ${totals.warnings} ${pluralize( + 'warning', + totals.warnings + )}.${RESET_ESCAPE_CODE}` + ) + ); + } +} + +export function printStepDetails({ + testNameToDisplay, + checks, + verboseLogs, + verboseResponseLogs, +}: { + testNameToDisplay: string; + checks: Check[]; + verboseLogs?: VerboseLog; + verboseResponseLogs?: VerboseLog; +}) { + printStepSeparatorLine(); + displayChecks(testNameToDisplay, checks, verboseLogs, verboseResponseLogs); +} diff --git a/packages/respect-core/src/utils/delay.ts b/packages/respect-core/src/utils/delay.ts new file mode 100644 index 0000000000..bb5467eb31 --- /dev/null +++ b/packages/respect-core/src/utils/delay.ts @@ -0,0 +1,4 @@ +/* istanbul ignore file */ +export function delay(ms = 0) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/packages/respect-core/src/utils/env.ts b/packages/respect-core/src/utils/env.ts new file mode 100644 index 0000000000..8721a53625 --- /dev/null +++ b/packages/respect-core/src/utils/env.ts @@ -0,0 +1,11 @@ +export function isBrowser() { + return ( + typeof window !== 'undefined' || + typeof process === 'undefined' || + (process?.platform as any) === 'browser' + ); // main and worker thread +} + +export function env() { + return isBrowser() ? {} : process.env || {}; +} diff --git a/packages/respect-core/src/utils/file.ts b/packages/respect-core/src/utils/file.ts new file mode 100644 index 0000000000..c28aa66596 --- /dev/null +++ b/packages/respect-core/src/utils/file.ts @@ -0,0 +1,3 @@ +export function isTestFile(path: string, fileContent: any) { + return /\.(yaml|yml|json)$/i.test(path) && !!fileContent.arazzo; +} diff --git a/packages/respect-core/src/utils/get-nested-value.ts b/packages/respect-core/src/utils/get-nested-value.ts new file mode 100644 index 0000000000..dc7912fbe8 --- /dev/null +++ b/packages/respect-core/src/utils/get-nested-value.ts @@ -0,0 +1,11 @@ +export function getNestedValue(obj: any, path: string[]): any { + let current = obj; + for (const key of path) { + if (current && current[key] !== undefined) { + current = current[key]; + } else { + return undefined; + } + } + return current; +} diff --git a/packages/respect-core/src/utils/get-reunite-url.ts b/packages/respect-core/src/utils/get-reunite-url.ts new file mode 100644 index 0000000000..cfe5b2487f --- /dev/null +++ b/packages/respect-core/src/utils/get-reunite-url.ts @@ -0,0 +1,17 @@ +const reuniteUrls = { + us: 'https://app.cloud.redocly.com', + eu: 'https://app.cloud.eu.redocly.com', +} as const; + +export function getReuniteUrl(residency?: string) { + if (!residency) residency = 'us'; + + let reuniteUrl: string = reuniteUrls[residency as keyof typeof reuniteUrls]; + + if (!reuniteUrl) { + reuniteUrl = residency; + } + + const url = new URL('/api', reuniteUrl).toString(); + return url; +} diff --git a/packages/respect-core/src/utils/har-logs/README.md b/packages/respect-core/src/utils/har-logs/README.md new file mode 100644 index 0000000000..98e2a0f1a0 --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/README.md @@ -0,0 +1,9 @@ +# HAR Logs processing wrapper + +Forked from: https://github.com/exogen/node-fetch-har + +Changes made: + +- Allow to pass body as FormData +- update nanoid to 3.1.20 +- migrated to be used with undici diff --git a/packages/respect-core/src/utils/har-logs/create-har-log.ts b/packages/respect-core/src/utils/har-logs/create-har-log.ts new file mode 100644 index 0000000000..e7133fea59 --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/create-har-log.ts @@ -0,0 +1,28 @@ +const { name: packageName, version: packageVersion } = require('../../../package.json'); + +export function createHarLog(entries: any[] = [], pageInfo: any = {}): any { + return { + log: { + version: '1.2', + creator: { + name: packageName, + version: packageVersion, + }, + pages: [ + Object.assign( + { + startedDateTime: new Date().toISOString(), + id: 'page_1', + title: 'Page', + pageTimings: { + onContentLoad: -1, + onLoad: -1, + }, + }, + pageInfo + ), + ], + entries, + }, + }; +} diff --git a/packages/respect-core/src/utils/har-logs/helpers/add-headers.ts b/packages/respect-core/src/utils/har-logs/helpers/add-headers.ts new file mode 100644 index 0000000000..49a0557933 --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/helpers/add-headers.ts @@ -0,0 +1,14 @@ +export function addHeaders(oldHeaders: any, newHeaders: any): any { + if (!oldHeaders) { + return newHeaders; + } else if (typeof oldHeaders.set === 'function' && typeof oldHeaders.constructor === 'function') { + const Headers = oldHeaders.constructor; + const headers = new Headers(oldHeaders); + for (const name in newHeaders) { + headers.set(name, newHeaders[name]); + } + return headers; + } else { + return Object.assign({}, oldHeaders, newHeaders); + } +} diff --git a/packages/respect-core/src/utils/har-logs/helpers/build-headers.ts b/packages/respect-core/src/utils/har-logs/helpers/build-headers.ts new file mode 100644 index 0000000000..30fe15d6d7 --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/helpers/build-headers.ts @@ -0,0 +1,29 @@ +/** + * Support the three possible header formats we'd get from a request or + * response: + * + * - A flat array with both names and values: [name, value, name, value, ...] + * - An object with array values: { name: [value, value] } + * - An object with string values: { name: value } + */ +export function buildHeaders(headers: any): any[] { + const list = []; + if (Array.isArray(headers)) { + for (let i = 0; i < headers.length; i += 2) { + list.push({ + name: headers[i], + value: headers[i + 1], + }); + } + } else if (headers instanceof Map || headers.entries) { + // Handle both Map and Headers objects (which have entries()) + for (const [name, value] of headers.entries()) { + list.push({ name, value }); + } + } else if (typeof headers === 'object') { + for (const [name, value] of Object.entries(headers)) { + list.push({ name, value: value as string }); + } + } + return list; +} diff --git a/packages/respect-core/src/utils/har-logs/helpers/build-params.ts b/packages/respect-core/src/utils/har-logs/helpers/build-params.ts new file mode 100644 index 0000000000..08e8689470 --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/helpers/build-params.ts @@ -0,0 +1,17 @@ +import * as querystring from 'querystring'; + +export function buildParams(paramString: string): any[] { + const params = []; + const parsed = querystring.parse(paramString); + for (const name in parsed) { + const value = parsed[name]; + if (Array.isArray(value)) { + value.forEach((item) => { + params.push({ name, value: item }); + }); + } else { + params.push({ name, value }); + } + } + return params; +} diff --git a/packages/respect-core/src/utils/har-logs/helpers/build-request-cookies.ts b/packages/respect-core/src/utils/har-logs/helpers/build-request-cookies.ts new file mode 100644 index 0000000000..c133a575dc --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/helpers/build-request-cookies.ts @@ -0,0 +1,18 @@ +import * as cookie from 'cookie'; + +export function buildRequestCookies(headers: any): any[] { + const cookies: any[] = []; + for (const [header, headerValue] of Object.entries(headers)) { + if (header.toLowerCase() === 'cookie') { + // Handle both array and single string cases + const cookieValues = Array.isArray(headerValue) ? headerValue : [headerValue]; + for (const value of cookieValues) { + const parsed = cookie.parse(value); + for (const [name, value] of Object.entries(parsed)) { + cookies.push({ name, value }); + } + } + } + } + return cookies; +} diff --git a/packages/respect-core/src/utils/har-logs/helpers/build-response-cookies.ts b/packages/respect-core/src/utils/har-logs/helpers/build-response-cookies.ts new file mode 100644 index 0000000000..8612ae4e0f --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/helpers/build-response-cookies.ts @@ -0,0 +1,41 @@ +import * as setCookie from 'set-cookie-parser'; + +export function buildResponseCookies(headers: any): any[] { + const cookies: any[] = []; + // Handle both plain objects and Headers instance + const setCookies = + headers instanceof Headers + ? headers.getSetCookie() // For Headers object + : headers['set-cookie']; // For plain objects + + if (setCookies) { + for (const headerValue of setCookies) { + let parsed; + try { + parsed = setCookie.parse(headerValue); + } catch { + continue; + } + for (const cookie of parsed) { + const { name, value, path, domain, expires, httpOnly, secure } = cookie; + const harCookie: any = { + name, + value, + httpOnly: httpOnly || false, + secure: secure || false, + }; + if (path) { + harCookie.path = path as string | undefined; + } + if (domain) { + harCookie.domain = domain as string | undefined; + } + if (expires) { + harCookie.expires = expires.toISOString(); + } + cookies.push(harCookie); + } + } + } + return cookies; +} diff --git a/packages/respect-core/src/utils/har-logs/helpers/get-agent.ts b/packages/respect-core/src/utils/har-logs/helpers/get-agent.ts new file mode 100644 index 0000000000..a95be53a0c --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/helpers/get-agent.ts @@ -0,0 +1,99 @@ +import * as http from 'http'; +import * as https from 'https'; +import { Dispatcher } from 'undici'; +import { getInputUrl } from './get-input-url'; +import { handleRequest } from './handle-request'; + +const HarHttpAgent = createAgentClass(http.Agent); +const HarHttpsAgent = createAgentClass(https.Agent); + +let globalHttpAgent: any; +let globalHttpsAgent: any; + +// Add new Undici dispatcher +class HarDispatcher extends Dispatcher { + constructor(opts?: any) { + super(opts); + } + + dispatch(options: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean { + // Handle HAR logging here similar to handleRequest + handleRequest({ input: options, handler, harLog: new Map(), isUndici: true }); + return super.dispatch(options, handler); + } +} + +export function getAgent(input: any, options: any): any { + // Add Undici dispatcher support + if (options.dispatcher) { + if (options.dispatcher instanceof Dispatcher) { + return new HarDispatcher(options.dispatcher); + } + return options.dispatcher; + } + + if (options.agent) { + if (typeof options.agent === 'function') { + return function (...args: any[]) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const agent = options.agent.call(this, ...args); + if (agent) { + instrumentAgentInstance(agent); + return agent; + } + return getGlobalAgent(input); + }; + } + instrumentAgentInstance(options.agent); + return options.agent; + } + return getGlobalAgent(input); +} + +function getGlobalAgent(input: any): any { + const url = getInputUrl(input); + if (url.protocol === 'http:') { + if (!globalHttpAgent) { + globalHttpAgent = new HarHttpAgent(); + } + return globalHttpAgent; + } + if (!globalHttpsAgent) { + globalHttpsAgent = new HarHttpsAgent(); + } + return globalHttpsAgent; +} + +/** + * Instrument an existing Agent instance. This overrides the instance's + * `addRequest` method. It should be fine to continue using for requests made + * without `withHar` - if the request doesn't have our `x-har-request-id` + * header, it won't do anything extra. + */ +function instrumentAgentInstance(agent: any): void { + const { addRequest: originalAddRequest } = agent; + if (!originalAddRequest.isHarEnabled) { + agent.addRequest = function addRequest(request: any, ...args: []) { + handleRequest(request, ...args); + return originalAddRequest.call(this, request, ...args); + }; + agent.addRequest.isHarEnabled = true; + } +} + +function createAgentClass(BaseAgent: any): any { + class HarAgent extends BaseAgent { + constructor(...args: any[]) { + super(...args); + (this.addRequest as any).isHarEnabled = true; + } + + addRequest(request: any, ...args: []): void { + handleRequest(request, ...args); + return super.addRequest(request, ...args); + } + } + + return HarAgent; +} diff --git a/packages/respect-core/src/utils/har-logs/helpers/get-duration.ts b/packages/respect-core/src/utils/har-logs/helpers/get-duration.ts new file mode 100644 index 0000000000..abfd72d501 --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/helpers/get-duration.ts @@ -0,0 +1,5 @@ +export function getDuration(a: [number, number], b: [number, number]): number { + const seconds = b[0] - a[0]; + const nanoseconds = b[1] - a[1]; + return seconds * 1000 + nanoseconds / 1e6; +} diff --git a/packages/respect-core/src/utils/har-logs/helpers/get-input-url.ts b/packages/respect-core/src/utils/har-logs/helpers/get-input-url.ts new file mode 100644 index 0000000000..38f2c64f28 --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/helpers/get-input-url.ts @@ -0,0 +1,4 @@ +export function getInputUrl(input: any): URL { + const url = typeof input === 'string' ? input : input.url; + return new URL(url); +} diff --git a/packages/respect-core/src/utils/har-logs/helpers/handle-request.ts b/packages/respect-core/src/utils/har-logs/helpers/handle-request.ts new file mode 100644 index 0000000000..74df4823c8 --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/helpers/handle-request.ts @@ -0,0 +1,243 @@ +import type { Dispatcher } from 'undici'; +import type { Har, Entry } from 'har-format'; +import type { IncomingHttpHeaders } from 'http'; + +interface HarEntry extends Entry { + _timestamps?: { + start: number; + connect: number; + firstByte: number; + }; +} + +export function handleRequest({ + input, + handler, + harLog, + isUndici = false, +}: { + input: Request | Dispatcher.DispatchOptions; + handler: Dispatcher.DispatchHandlers | NodeJS.EventEmitter; + harLog?: Map; + isUndici?: boolean; +}): Dispatcher.DispatchHandlers | NodeJS.EventEmitter { + if (isUndici) { + return handleUndiciRequest( + input as Dispatcher.DispatchOptions, + handler as Dispatcher.DispatchHandlers, + harLog + ); + } else { + handleNodeRequest(input as Request, handler as NodeJS.EventEmitter, harLog); + return handler as NodeJS.EventEmitter; + } +} + +function handleUndiciRequest( + options: Dispatcher.DispatchOptions, + handler: Dispatcher.DispatchHandlers, + harLog?: Map +): Dispatcher.DispatchHandlers { + const startTime = Date.now(); + const requestId = generateRequestId(); + + const harEntry: HarEntry = { + startedDateTime: new Date().toISOString(), + time: -1, + request: { + method: options.method || 'GET', + url: options.path || '', + httpVersion: 'HTTP/1.1', + cookies: [], + headers: formatUndiciHeaders(options.headers as any), + queryString: [], + headersSize: -1, + bodySize: -1, + }, + response: { + status: 0, + statusText: '', + httpVersion: 'HTTP/1.1', + cookies: [], + headers: [], + content: { + size: 0, + mimeType: '', + text: '', + }, + redirectURL: '', + headersSize: -1, + bodySize: -1, + }, + cache: {}, + timings: { + blocked: -1, + dns: -1, + connect: -1, + send: 0, + wait: 0, + receive: 0, + ssl: -1, + }, + }; + + const wrappedHandler: Dispatcher.DispatchHandlers = { + onConnect: handler.onConnect, + onError: (error) => { + harEntry.response.status = 0; + harEntry.response.statusText = error.message; + harEntry.time = Date.now() - startTime; + if (harLog) { + harLog.set(requestId, { + log: { + version: '1.2', + creator: { name: 'api-test-framework', version: '1.0' }, + entries: [harEntry], + }, + }); + } + if (handler.onError) { + handler.onError(error); + } + }, + onHeaders: (statusCode, headers, resume, opaque) => { + harEntry.response.status = statusCode; + harEntry.response.headers = formatUndiciHeaders(headers as any); + if (handler.onHeaders) { + handler.onHeaders(statusCode, headers, resume, opaque); + } + return true; + }, + onData: handler.onData, + onComplete: (trailers) => { + harEntry.time = Date.now() - startTime; + if (harLog) { + harLog.set(requestId, { + log: { + version: '1.2', + creator: { name: 'api-test-framework', version: '1.0' }, + entries: [harEntry], + }, + }); + } + if (handler.onComplete) { + handler.onComplete(trailers); + } + }, + }; + + return wrappedHandler; +} + +function formatUndiciHeaders( + headers: Record | string[] | IncomingHttpHeaders | null | undefined +): Array<{ name: string; value: string }> { + if (!headers) return []; + + if (Array.isArray(headers)) { + return chunks(headers, 2).map(([name, value]) => ({ + name: String(name), + value: String(value), + })); + } + + return []; +} + +function chunks(arr: T[], size: number): T[][] { + return Array.from({ length: Math.ceil(arr.length / size) }, (_, i) => + arr.slice(i * size, i * size + size) + ); +} + +function generateRequestId(): string { + return Math.random().toString(36).substring(2, 15); +} + +function handleNodeRequest(request: Request, handler: any, harLog?: Map): void { + const requestId = generateRequestId(); + const harEntry: Entry = { + startedDateTime: new Date().toISOString(), + time: 0, + request: { + method: request.method || 'GET', + url: request.url, + httpVersion: 'HTTP/1.1', + cookies: [], + headers: formatUndiciHeaders(request.headers as any), + queryString: [], + headersSize: -1, + bodySize: -1, + }, + response: { + status: 0, + statusText: '', + httpVersion: 'HTTP/1.1', + cookies: [], + headers: [], + content: { + size: 0, + mimeType: '', + text: '', + }, + redirectURL: '', + headersSize: -1, + bodySize: -1, + }, + cache: {}, + timings: { + blocked: -1, + dns: -1, + connect: -1, + send: -1, + wait: -1, + receive: -1, + ssl: -1, + }, + }; + // Add HAR tracking header + if (request.headers instanceof Headers) { + request.headers.set('x-har-request-id', requestId); + } else if (typeof request.headers === 'object' && request.headers !== null) { + (request.headers as Record)['x-har-request-id'] = requestId; + } + + // Register all required event handlers + handler.on('response', (response: any) => { + harEntry.response.status = response.statusCode; + harEntry.response.statusText = response.statusMessage; + harEntry.response.headers = formatUndiciHeaders(response.headers); + }); + + handler.on('data', (chunk: Buffer) => { + // Handle response data chunks + harEntry.response.content.text = (harEntry.response.content.text || '') + chunk.toString(); + harEntry.response.content.size += chunk.length; + }); + + handler.on('end', () => { + if (harLog) { + harLog.set(requestId, { + log: { + version: '1.2', + creator: { name: 'api-test-framework', version: '1.0' }, + entries: [harEntry], + }, + }); + } + }); + + handler.on('error', (error: Error) => { + harEntry.response.status = 0; + harEntry.response.statusText = error.message; + if (harLog) { + harLog.set(requestId, { + log: { + version: '1.2', + creator: { name: 'api-test-framework', version: '1.0' }, + entries: [harEntry], + }, + }); + } + }); +} diff --git a/packages/respect-core/src/utils/har-logs/index.ts b/packages/respect-core/src/utils/har-logs/index.ts new file mode 100644 index 0000000000..1e074bda7a --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/index.ts @@ -0,0 +1,2 @@ +export * from './create-har-log'; +export * from './with-har'; diff --git a/packages/respect-core/src/utils/har-logs/with-har.ts b/packages/respect-core/src/utils/har-logs/with-har.ts new file mode 100644 index 0000000000..de218541f1 --- /dev/null +++ b/packages/respect-core/src/utils/har-logs/with-har.ts @@ -0,0 +1,234 @@ +/* istanbul ignore file */ + +// Forked from: +// https://github.com/exogen/node-fetch-har +// Changes made: +// - Allow to pass body as FormData +// - removed nanoid and replaced with crypto.randomUUID +// - migrated to be used with undici + +import { URL } from 'url'; +import { Client } from 'undici'; +import * as crypto from 'node:crypto'; +import { addHeaders } from './helpers/add-headers'; +import { getDuration } from './helpers/get-duration'; +import { buildRequestCookies } from './helpers/build-request-cookies'; +import { buildHeaders } from './helpers/build-headers'; +import { buildResponseCookies } from './helpers/build-response-cookies'; + +const HAR_HEADER_NAME = 'x-har-request-id'; +const harEntryMap = new Map(); +export interface WithHar { + (baseFetch: any, defaults?: any): any; + harEntryMap?: Map; +} + +export const withHar: WithHar = function (baseFetch: any, defaults: any = {}): any { + withHar.harEntryMap = harEntryMap; + return async function fetch(input: any, options: any = {}): Promise { + const { + har = defaults.har, + harPageRef = defaults.harPageRef, + onHarEntry = defaults.onHarEntry, + } = options; + + if (har === false) { + return baseFetch(input, options); + } + + const requestId = crypto.randomUUID(); + const startTime = process.hrtime(); + + const url = new URL(typeof input === 'string' ? input : input.url); + + const entry = { + _compressed: false, + _resourceType: 'fetch', + _timestamps: { + start: startTime, + socket: startTime, + lookup: startTime, + connect: startTime, + secureConnect: startTime, + sent: startTime, + firstByte: startTime, + received: startTime, + }, + timings: { + blocked: -1, + dns: -1, + connect: -1, + send: 0, + wait: 0, + receive: 0, + ssl: -1, + }, + time: 0, + startedDateTime: new Date().toISOString(), + cache: { + beforeRequest: null, + afterRequest: null, + }, + request: { + method: options.method || 'GET', + url: url.href, + cookies: buildRequestCookies(options.headers || {}), + headers: buildHeaders(options.headers || {}), + queryString: [...url.searchParams].map(([name, value]) => ({ + name, + value, + })), + headersSize: -1, + bodySize: -1, + postData: {}, + httpVersion: 'HTTP/1.1', + }, + response: {}, + pageref: '', + }; + + // Replace the Dispatcher initialization with Client + const client = options.dispatcher || new Client(url.origin); + + // Listen to Undici dispatcher events + client.on('connect', () => (entry._timestamps.connect = process.hrtime())); + + // Pass the dispatcher in options + options = Object.assign({}, options, { + headers: addHeaders(options.headers, { [HAR_HEADER_NAME]: requestId }), + dispatcher: client, // Use client as dispatcher + }); + + harEntryMap.set(requestId, entry); + + // Update sent time just before the request + entry._timestamps.sent = process.hrtime(); + + // Make the request + const response = await baseFetch(input, options); + + // Need to clone response to get both text and arrayBuffer + const responseClone = response.clone(); + + // Update firstByte time when we get the response + entry._timestamps.firstByte = process.hrtime(); + + // Get the response body and update received time + const text = await response.text(); + entry._timestamps.received = process.hrtime(); + + const harEntry = harEntryMap.get(requestId); + harEntryMap.delete(requestId); + + if (!harEntry) { + return response; + } + + // Add response info + if (!harEntry.response) { + harEntry.response = {}; + } + + // Calculate total bytes of headers including the double CRLF before body + const headerLines = [...response.headers.entries()].map(([name, value]) => `${name}: ${value}`); + const statusLine = `HTTP/1.1 ${response.status} ${response.statusText}`; + const headerBytes = Buffer.byteLength( + statusLine + '\r\n' + headerLines.join('\r\n') + '\r\n\r\n' + ); + harEntry._compressed = /^(gzip|compress|deflate|br)$/.test( + response.headers.get('content-encoding') || '' + ); + + if (!harEntry.response.content) { + harEntry.response.content = { + size: -1, + }; + } + + if (harEntry._compressed) { + const rawBody = await responseClone.arrayBuffer(); + harEntry.response.content.size = rawBody.byteLength; + } else { + harEntry.response.content.size = text ? Buffer.byteLength(text) : -1; + } + const bodySize = text ? Buffer.byteLength(text) : -1; + + harEntry.response = { + headers: buildHeaders(response.headers), + cookies: buildResponseCookies(response.headers), + status: response.status, + statusText: response.statusText || '', + httpVersion: response.httpVersion ? `HTTP/${response.httpVersion}` : 'HTTP/1.1', + redirectURL: response.headers.location || '', + content: { + size: + harEntry._compressed && harEntry.response.content.size !== -1 + ? harEntry.response.content.size + : Buffer.byteLength(text), + mimeType: response.headers.get('content-type') || '', + text, + compression: + harEntry._compressed && harEntry.response.content.size !== -1 + ? harEntry.response.content.size - bodySize + : 0, + }, + bodySize, + headersSize: headerBytes, + }; + + // Calculate timings + const { _timestamps: time } = harEntry; + harEntry.timings = { + blocked: Math.max(getDuration(time.start, time.socket), 0.01), + dns: -1, + connect: Math.max(getDuration(time.lookup, time.connect), -1), + ssl: time.secureConnect ? Math.max(getDuration(time.connect, time.secureConnect), -1) : -1, + send: getDuration(time.secureConnect || time.connect, time.sent), + wait: Math.max(getDuration(time.sent, time.firstByte), 0), + receive: getDuration(time.firstByte, time.received), + }; + + // Calculate total time + harEntry.time = getDuration(time.start, time.received); + + const parents = []; + let child = harEntry; + do { + const parent = child._parent; + delete child._parent; + if (parent) { + parents.unshift(parent); + } + child = parent; + } while (child); + + // Allow grouping by pages. + entry.pageref = harPageRef || 'page_1'; + parents.forEach((parent) => { + parent.pageref = entry.pageref; + }); + + const Response = + defaults.Response || baseFetch.Response || global.Response || response.constructor; + const responseCopy = new Response(text, { + status: response.statusCode, + statusText: response.statusText || '', + headers: response.headers, + url: response.url, + }); + responseCopy.harEntry = entry; + + if (har && typeof har === 'object') { + har.log.entries.push(...parents, entry); + } + + if (onHarEntry) { + parents.forEach((parent) => { + onHarEntry(parent); + }); + onHarEntry(entry); + } + + return responseCopy; + }; +}; diff --git a/packages/respect-core/src/utils/is-empty.ts b/packages/respect-core/src/utils/is-empty.ts new file mode 100644 index 0000000000..cafef4dcc2 --- /dev/null +++ b/packages/respect-core/src/utils/is-empty.ts @@ -0,0 +1,15 @@ +export function isEmpty(value: any) { + if (value === null || value === undefined) { + return true; + } + + if (typeof value === 'object') { + return Object.keys(value).length === 0; + } + + if (typeof value === 'string' || Array.isArray(value)) { + return value.length === 0; + } + + return false; +} diff --git a/packages/respect-core/src/utils/is-json.ts b/packages/respect-core/src/utils/is-json.ts new file mode 100644 index 0000000000..0c7c19cd40 --- /dev/null +++ b/packages/respect-core/src/utils/is-json.ts @@ -0,0 +1,8 @@ +export function isJSON(str: string): boolean { + try { + JSON.parse(str); + return true; + } catch { + return false; + } +} diff --git a/packages/respect-core/src/utils/is-url.ts b/packages/respect-core/src/utils/is-url.ts new file mode 100644 index 0000000000..4b34f3958e --- /dev/null +++ b/packages/respect-core/src/utils/is-url.ts @@ -0,0 +1,13 @@ +const throws = (fn: () => unknown) => { + try { + fn(); + } catch { + return true; + } + + return false; +}; + +export function isURL(url: string) { + return !throws(() => new URL(url)); +} diff --git a/packages/respect-core/src/utils/logger/logger.ts b/packages/respect-core/src/utils/logger/logger.ts new file mode 100644 index 0000000000..7ae484b221 --- /dev/null +++ b/packages/respect-core/src/utils/logger/logger.ts @@ -0,0 +1,44 @@ +import { gray } from 'colorette'; + +const RESET_ESCAPE_CODE_IN_TERMINAL = '\x1B[0m'; + +export interface Logger { + log(message: string): void; + error(message: string): void; + printNewLine(): void; + printSeparator(separator: string): void; +} + +export class DefaultLogger implements Logger { + private static instance: DefaultLogger; + + private constructor() {} + + public static getInstance(): DefaultLogger { + if (!DefaultLogger.instance) { + DefaultLogger.instance = new DefaultLogger(); + } + return DefaultLogger.instance; + } + + error(message: string): void { + process.stderr.write(`${message}\n`); + } + + log(message: string): void { + process.stdout.write(`${message}`); + } + + printNewLine(): void { + process.stdout.write(`${RESET_ESCAPE_CODE_IN_TERMINAL}\n`); + } + + printSeparator(separator: string) { + const windowWidth = process.stdout.columns || 80; + const separatorLine = separator + .repeat(Math.ceil(windowWidth / separator.length)) + .slice(0, windowWidth); + + process.stdout.write(gray(`${separatorLine}`)); + } +} diff --git a/packages/respect-core/src/utils/mtls/create-mtls-client.ts b/packages/respect-core/src/utils/mtls/create-mtls-client.ts new file mode 100644 index 0000000000..134ae26a39 --- /dev/null +++ b/packages/respect-core/src/utils/mtls/create-mtls-client.ts @@ -0,0 +1,25 @@ +import { Client } from 'undici'; + +import type { TestContext } from '../../types'; + +export function createMtlsClient( + parsedPathToFetch: string, + mtlsCerts: TestContext['mtlsCerts'] = {} +) { + const { clientCert, clientKey, caCert } = mtlsCerts; + const baseUrl = new URL(parsedPathToFetch).origin; + + if (clientCert && clientKey) { + return new Client(baseUrl, { + connect: { + key: Buffer.from(clientKey), + cert: Buffer.from(clientCert), + ...(caCert && { ca: Buffer.from(caCert) }), + // Keeping this `false` to have the ability to call different servers in one Arazzo file + // some of them might not require mTLS. + rejectUnauthorized: false, + }, + }); + } + return undefined; +} diff --git a/packages/respect-core/src/utils/mtls/resolve-mtls-certificates.ts b/packages/respect-core/src/utils/mtls/resolve-mtls-certificates.ts new file mode 100644 index 0000000000..c2a1cf3969 --- /dev/null +++ b/packages/respect-core/src/utils/mtls/resolve-mtls-certificates.ts @@ -0,0 +1,54 @@ +import * as fs from 'node:fs'; +import { type TestContext } from '../../types'; + +export function resolveMtlsCertificates(mtlsCertificates: Partial = {}) { + const { clientCert, clientKey, caCert } = mtlsCertificates; + + return { + clientCert: resolveCertificate(clientCert), + clientKey: resolveCertificate(clientKey), + caCert: resolveCertificate(caCert), + }; +} + +function resolveCertificate(cert: string | undefined): string | undefined { + if (!cert) return undefined; + + try { + // Check if the string looks like a certificate content + const isCertContent = cert.includes('-----BEGIN') && cert.includes('-----END'); + + if (!isCertContent) { + // If not a certificate content, treat as file path + fs.accessSync(cert, fs.constants.R_OK); + return fs.readFileSync(cert, 'utf-8'); + } + + // Return the certificate content as-is + return formatCertificate(cert); + } catch (error: any) { + throw new Error(`Failed to read certificate: ${error.message}`); + } +} + +function formatCertificate(cert: string): string { + // Split the content into header, body, and footer + const matches = cert.match( + /^(-----BEGIN[^-]+-----)\r?\n([A-Za-z0-9+/=\r\n\t ]+)\r?\n(-----END[^-]+-----)/ + ); + if (!matches) { + throw new Error('Invalid certificate format'); + } + + const [, header, body, footer] = matches; + + // Format the body with proper line breaks (64 characters per line) + const formattedBody = + body + .replace(/\s+/g, '') // Remove all whitespace + .match(/.{1,64}/g) // Split into 64-character chunks + ?.join('\n') || ''; // Join with newlines + + // Reconstruct the properly formatted certificate + return `${header}\n${formattedBody}\n${footer}`; +} diff --git a/packages/respect-core/src/utils/sort.ts b/packages/respect-core/src/utils/sort.ts new file mode 100644 index 0000000000..289b7b6dfd --- /dev/null +++ b/packages/respect-core/src/utils/sort.ts @@ -0,0 +1,28 @@ +const methodCompareMap = { + post: 0, + put: 1, + get: 2, + patch: 3, + delete: 4, + head: 5, + options: 6, + trace: 7, + connect: 8, + query: 9, +}; + +function getKeyWeigh(keyMap: Record, key: string): number { + const uniqueKeyMap = new Map(Object.entries(keyMap)); + const ketToReturn = uniqueKeyMap.get(key.toLowerCase()); + return ketToReturn ? ketToReturn : -1; +} + +function sortKeys(a: string, b: string, compareMap: Record): number { + const aKeyWeight = getKeyWeigh(compareMap, a); + const bKeyWeight = getKeyWeigh(compareMap, b); + return aKeyWeight - bKeyWeight; +} + +export function sortMethods(a: string, b: string) { + return sortKeys(a, b, methodCompareMap); +} diff --git a/packages/respect-core/src/utils/time.ts b/packages/respect-core/src/utils/time.ts new file mode 100644 index 0000000000..f7223a096b --- /dev/null +++ b/packages/respect-core/src/utils/time.ts @@ -0,0 +1,6 @@ +// Function from @redocly/cli +export function getExecutionTime(startedAt: number) { + return process.env.NODE_ENV === 'test' + ? 'ms' + : `${Math.ceil(performance.now() - startedAt)}ms`; +} diff --git a/packages/respect-core/src/utils/url.ts b/packages/respect-core/src/utils/url.ts new file mode 100644 index 0000000000..c9167312e0 --- /dev/null +++ b/packages/respect-core/src/utils/url.ts @@ -0,0 +1,5 @@ +export function combineUrl(host: string, path: string): string { + const normalizedHost = host.endsWith('/') ? host.slice(0, -1) : host; + const normalizedPath = path.startsWith('/') ? path.slice(1) : path; + return `${normalizedHost}/${normalizedPath}`; +} diff --git a/packages/respect-core/src/utils/yaml.ts b/packages/respect-core/src/utils/yaml.ts new file mode 100644 index 0000000000..d8354da8be --- /dev/null +++ b/packages/respect-core/src/utils/yaml.ts @@ -0,0 +1,23 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore broken types for js-yaml +import { JSON_SCHEMA, types, load, dump } from 'js-yaml'; +import { readFileSync } from 'fs'; + +import type { LoadOptions, DumpOptions } from 'js-yaml'; + +const DEFAULT_SCHEMA_WITHOUT_TIMESTAMP = JSON_SCHEMA.extend({ + implicit: [types.merge], + explicit: [types.binary, types.omap, types.pairs, types.set], +}); + +export function parseYaml(str: string, opts?: LoadOptions): unknown { + return load(str, { schema: DEFAULT_SCHEMA_WITHOUT_TIMESTAMP, ...opts }); +} + +export function stringifyYaml(obj: any, opts?: DumpOptions): string { + return dump(obj, opts); +} + +export function readYaml(path: string): unknown { + return parseYaml(readFileSync(path, 'utf-8')); +} diff --git a/packages/respect-core/tsconfig.json b/packages/respect-core/tsconfig.json new file mode 100644 index 0000000000..2a475cef2a --- /dev/null +++ b/packages/respect-core/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "target": "ESNext", + "module": "CommonJS", + "rootDir": "src", + "resolveJsonModule": true, + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "outDir": "lib", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitAny": true, + "moduleResolution": "node", + "skipLibCheck": true, + "allowJs": true + }, + "references": [{ "path": "../core" }], + "include": [ + "src/**/*.ts", + "src/**/*.js" // Add this line + ], + "exclude": ["**/__tests__", "**/__mocks__"] +} diff --git a/resources/museum-api-test.yaml b/resources/museum-api-test.yaml new file mode 100644 index 0000000000..ab2ce44844 --- /dev/null +++ b/resources/museum-api-test.yaml @@ -0,0 +1,122 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API + description: >- + An imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + - name: tickets-from-museum-api + type: arazzo + url: museum-tickets.yaml + +workflows: + - workflowId: get-museum-hours + description: >- + This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from museum-api.yaml description. + operationId: museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + outputs: + schedule: $response.body + - stepId: buy-ticket + description: >- + Buy a ticket for the museum by calling an external workflow from another Arazzo file. + workflowId: $sourceDescriptions.tickets-from-museum-api.workflows.get-museum-tickets + outputs: + ticketId: $outputs.ticketId + - workflowId: events-crud + description: >- + This workflow demonstrates how to list, create, update, and delete special events at the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: list-events + description: >- + Request the list of events. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/get' + outputs: + events: $response.body + - stepId: create-event + description: >- + Create a new special event. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events/post' + requestBody: + payload: + name: 'Mermaid Treasure Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.' + dates: + - $steps.list-events.outputs.events.0.dates.0 + - $steps.list-events.outputs.events.0.dates.1 + price: 0 + successCriteria: + - condition: $statusCode == 201 + - context: $response.body + condition: $.name == 'Mermaid Treasure Identification and Analysis' + type: jsonpath + outputs: + createdEventId: $response.body#/eventId + name: $response.body#/name + + - stepId: get-event-by-id + description: >- + Get the details of the event that was created in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/get' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + successCriteria: + - context: $statusCode + condition: '^200$' + type: regex + - stepId: update-event + description: >- + Update the created event with new details. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/patch' + parameters: + - name: eventId + in: path + value: $steps.create-event.outputs.createdEventId + requestBody: + payload: + name: 'Orca Identification and Analysis' + location: 'Under the seaaa 🦀 🎶 🌊.' + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + successCriteria: + - condition: $statusCode == 200 + - context: $response.body + condition: $.name == 'Orca Identification and Analysis' + type: + type: jsonpath + version: draft-goessner-dispatch-jsonpath-00 + outputs: + updatedEventId: $response.body#/eventId + - stepId: delete-event + description: >- + Delete the event that was updated in the previous step. + operationPath: '{$sourceDescriptions.museum-api.url}#/paths/~1special-events~1{eventId}/delete' + parameters: + - name: eventId + in: path + value: $steps.update-event.outputs.updatedEventId + successCriteria: + - condition: $statusCode == 204 diff --git a/resources/museum-api.yaml b/resources/museum-api.yaml new file mode 100644 index 0000000000..f19af26a56 --- /dev/null +++ b/resources/museum-api.yaml @@ -0,0 +1,855 @@ +openapi: 3.1.0 +info: + title: Redocly Museum API + description: >- + Imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.1.1 + termsOfService: https://redocly.com/subscription-agreement/ + contact: + email: team@redocly.com + url: https://redocly.com/docs/cli/ + license: + name: MIT + url: https://opensource.org/license/mit/ +servers: + - url: https://redocly.com/_mock/demo/openapi/museum-api/ +paths: + /museum-hours: + get: + summary: Get museum hours + description: Get upcoming museum operating hours. + operationId: getMuseumHours + tags: + - Operations + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/GetMuseumHoursResponse' + examples: + default_example: + $ref: '#/components/examples/GetMuseumHoursResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events: + post: + summary: Create special events + description: Creates a new special event for the museum. + operationId: createSpecialEvent + tags: + - Events + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/CreateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + get: + summary: List special events + description: Return a list of upcoming special events at the museum. + operationId: listSpecialEvents + tags: + - Events + parameters: + - $ref: '#/components/parameters/StartDate' + - $ref: '#/components/parameters/EndDate' + - $ref: '#/components/parameters/PaginationPage' + - $ref: '#/components/parameters/PaginationLimit' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSpecialEventsResponse' + examples: + default_example: + $ref: '#/components/examples/ListSpecialEventsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /special-events/{eventId}: + get: + summary: Get special event + description: Get details about a special event. + operationId: getSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/GetSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + patch: + summary: Update special event + description: Update the details of a special event. + operationId: updateSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSpecialEventRequest' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventRequestExample' + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/SpecialEventResponse' + examples: + default_example: + $ref: '#/components/examples/UpdateSpecialEventResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + delete: + summary: Delete special event + description: >- + Delete a special event from the collection. Allows museum to cancel + planned events. + operationId: deleteSpecialEvent + tags: + - Events + parameters: + - $ref: '#/components/parameters/EventId' + responses: + '204': + description: Success - no content. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + /tickets: + post: + summary: Buy museum tickets + description: Purchase museum tickets for general entry or special events. + operationId: buyMuseumTickets + tags: + - Tickets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsRequest' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsRequestExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsRequestExample' + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/BuyMuseumTicketsResponse' + examples: + general_entry: + $ref: '#/components/examples/BuyGeneralTicketsResponseExample' + event_entry: + $ref: '#/components/examples/BuyEventTicketsResponseExample' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /tickets/{ticketId}/qr: + get: + summary: Get ticket QR code + description: >- + Return an image of your ticket with scannable QR code. Used for event + entry. + operationId: getTicketCode + tags: + - Tickets + parameters: + - $ref: '#/components/parameters/TicketId' + responses: + '200': + description: Scannable event ticket in image format. + content: + image/png: + schema: + $ref: '#/components/schemas/GetTicketCodeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + /query: + query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseum + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + x-query: + summary: Query museum data + description: Execute a custom query against museum data + operationId: queryMuseumEx + parameters: + - in: header + name: Content-Type + required: true + schema: + type: string + enum: ['application/sql'] + description: Content type must be application/sql + - in: header + name: Accept + required: true + schema: + type: string + enum: ['text/csv'] + description: Accept header must be text/csv + tags: + - Operations + requestBody: + required: true + content: + application/sql: + schema: + type: string + description: SQL query to execute + example: 'select event_name, location, price from events where price < 50' + responses: + '200': + description: Query results + content: + text/csv: + schema: + type: string + format: binary + example: | + event_name,location,price + "Underwater Basket Weaving","Rec Center Pool",15 + "Cook like a Caveman","Fire Pit",5 + application/json: + schema: + type: array + items: + type: object + additionalProperties: true + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' +components: + schemas: + TicketType: + description: >- + Type of ticket being purchased. Use `general` for regular museum entry + and `event` for tickets to special events. + type: string + enum: + - event + - general + example: event + Date: + type: string + format: date + example: '2023-10-29' + Email: + description: Email address for ticket purchaser. + type: string + format: email + example: museum-lover@example.com + Phone: + description: Phone number for the ticket purchaser (optional). + type: string + example: +1(234)-567-8910 + BuyMuseumTicketsRequest: + description: Request payload used for purchasing museum tickets. + type: object + properties: + ticketType: + $ref: '#/components/schemas/TicketType' + eventId: + description: >- + Unique identifier for a special event. Required if purchasing + tickets for the museum's special events. + $ref: '#/components/schemas/EventId' + ticketDate: + description: Date that the ticket is valid for. + $ref: '#/components/schemas/Date' + email: + $ref: '#/components/schemas/Email' + phone: + $ref: '#/components/schemas/Phone' + required: + - ticketType + - ticketDate + - email + TicketMessage: + description: Confirmation message after a ticket purchase. + type: string + example: Museum general entry ticket purchased + TicketId: + description: Unique identifier for museum ticket. Generated when purchased. + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + TicketConfirmation: + description: Unique confirmation code used to verify ticket purchase. + type: string + example: ticket-event-a98c8f-7eb12 + BuyMuseumTicketsResponse: + description: Details for a museum ticket after a successful purchase. + type: object + properties: + message: + $ref: '#/components/schemas/TicketMessage' + eventName: + $ref: '#/components/schemas/EventName' + ticketId: + $ref: '#/components/schemas/TicketId' + ticketType: + $ref: '#/components/schemas/TicketType' + ticketDate: + description: Date the ticket is valid for. + $ref: '#/components/schemas/Date' + confirmationCode: + $ref: '#/components/schemas/TicketConfirmation' + required: + - message + - ticketId + - ticketType + - ticketDate + - confirmationCode + GetTicketCodeResponse: + description: Image of a ticket with a QR code used for museum or event entry. + type: string + format: binary + GetMuseumHoursResponse: + description: List of museum operating hours for consecutive days. + type: array + items: + $ref: '#/components/schemas/MuseumDailyHours' + MuseumDailyHours: + description: Daily operating hours for the museum. + type: object + properties: + date: + description: Date the operating hours apply to. + $ref: '#/components/schemas/Date' + example: '2024-12-31' + timeOpen: + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + description: >- + Time the museum opens on a specific date. Uses 24 hour time format + (`HH:mm`). + example: '09:00' + timeClose: + description: >- + Time the museum closes on a specific date. Uses 24 hour time format + (`HH:mm`). + type: string + pattern: ^([01]\d|2[0-3]):?([0-5]\d)$ + example: '18:00' + required: + - date + - timeOpen + - timeClose + EventId: + description: Identifier for a special event. + type: string + format: uuid + example: 3be6453c-03eb-4357-ae5a-984a0e574a54 + EventName: + type: string + description: Name of the special event. + example: Pirate Coding Workshop + EventLocation: + type: string + description: Location where the special event is held. + example: Computer Room + EventDescription: + type: string + description: Description of the special event. + example: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + EventDates: + type: array + items: + $ref: '#/components/schemas/Date' + description: List of planned dates for the special event. + EventPrice: + description: Price of a ticket for the special event. + type: number + format: float + example: 25 + CreateSpecialEventRequest: + description: Request payload for creating new special events at the museum. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - name + - location + - eventDescription + - dates + - price + UpdateSpecialEventRequest: + description: >- + Request payload for updating an existing special event. Only included + fields are updated in the event. + type: object + properties: + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + ListSpecialEventsResponse: + description: List of upcoming special events. + type: array + items: + $ref: '#/components/schemas/SpecialEventResponse' + SpecialEventResponse: + description: Information about a special event. + type: object + properties: + eventId: + $ref: '#/components/schemas/EventId' + name: + $ref: '#/components/schemas/EventName' + location: + $ref: '#/components/schemas/EventLocation' + eventDescription: + $ref: '#/components/schemas/EventDescription' + dates: + $ref: '#/components/schemas/EventDates' + price: + $ref: '#/components/schemas/EventPrice' + required: + - eventId + - name + - location + - eventDescription + - dates + - price + Error: + type: object + properties: + type: + type: string + example: object + title: + type: string + example: Validation failed + securitySchemes: + MuseumPlaceholderAuth: + type: http + scheme: basic + examples: + BuyGeneralTicketsRequestExample: + summary: General entry ticket + value: + ticketType: general + ticketDate: '2023-09-07' + email: todd@example.com + BuyEventTicketsRequestExample: + summary: Special event ticket + value: + ticketType: general + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + ticketDate: '2023-09-05' + email: todd@example.com + BuyGeneralTicketsResponseExample: + summary: General entry ticket + value: + message: Museum general entry ticket purchased + ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a + ticketType: general + ticketDate: '2023-09-07' + confirmationCode: ticket-general-e5e5c6-dce78 + BuyEventTicketsResponseExample: + summary: Special event ticket + value: + message: Museum special event ticket purchased + ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9 + eventName: Mermaid Treasure Identification and Analysis + ticketType: event + ticketDate: '2023-09-05' + confirmationCode: ticket-event-9c55eg-8v82a + CreateSpecialEventRequestExample: + summary: Create special event + value: + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 0 + CreateSpecialEventResponseExample: + summary: Special event created + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Under the seaaa 🦀 🎶 🌊. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + GetSpecialEventResponseExample: + summary: Get special event + value: + eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + ListSpecialEventsResponseExample: + summary: List of special events + value: + - eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97 + name: Sasquatch Ballet + location: Seattle... probably + eventDescription: >- + They're big, they're hairy, but they're also graceful. Come learn + how the biggest feet can have the lightest touch. + dates: + - '2023-12-15' + - '2023-12-22' + price: 40 + - eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483 + name: Solar Telescope Demonstration + location: Far from the sun. + eventDescription: Look at the sun without going blind! + dates: + - '2023-09-07' + - '2023-09-14' + price: 50 + - eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb + name: Cook like a Caveman + location: Fire Pit on East side + eventDescription: Learn to cook on an open flame. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + - eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910 + name: Underwater Basket Weaving + location: Rec Center Pool next door. + eventDescription: Learn to weave baskets underwater. + dates: + - '2023-09-12' + - '2023-09-15' + price: 15 + - eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: Room Sea-12 + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly + donated by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 30 + - eventId: 6744a0da-4121-49cd-8479-f8cc20526495 + name: Time Traveler Tea Party + location: Temporal Tearoom + eventDescription: Sip tea with important historical figures. + dates: + - '2023-11-18' + - '2023-11-25' + - '2023-12-02' + price: 60 + - eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54 + name: Pirate Coding Workshop + location: Computer Room + eventDescription: >- + Captain Blackbeard shares his love of the C...language. And possibly + Arrrrr (R lang). + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78 + name: Llama Street Art Through the Ages + location: Auditorium + eventDescription: Llama street art?! Alpaca my bags -- let's go! + dates: + - '2023-10-29' + - '2023-10-30' + - '2023-10-31' + price: 45 + - eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957 + name: The Great Parrot Debate + location: Outdoor Amphitheatre + eventDescription: See leading parrot minds discuss important geopolitical issues. + dates: + - '2023-11-03' + - '2023-11-10' + price: 35 + - eventId: b92d46b7-4c5d-422b-87a5-287767e26f29 + name: Eat a Bunch of Corn + location: Cafeteria + eventDescription: We accidentally bought too much corn. Please come eat it. + dates: + - '2023-11-10' + - '2023-11-17' + - '2023-11-24' + price: 5 + UpdateSpecialEventRequestExample: + summary: Update special event request + value: + location: On the beach. + price: 15 + UpdateSpecialEventResponseExample: + summary: Update special event + value: + eventId: dad4bce8-f5cb-4078-a211-995864315e39 + name: Mermaid Treasure Identification and Analysis + location: On the beach. + eventDescription: >- + Join us as we review and classify a rare collection of 20 + thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated + by Ariel. + dates: + - '2023-09-05' + - '2023-09-08' + price: 15 + GetMuseumHoursResponseExample: + summary: Get hours response + value: + - date: '2023-09-11' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-12' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-13' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-14' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-15' + timeOpen: '10:00' + timeClose: '16:00' + - date: '2023-09-18' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-19' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-20' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-21' + timeOpen: '09:00' + timeClose: '18:00' + - date: '2023-09-22' + timeOpen: '10:00' + timeClose: '16:00' + parameters: + PaginationPage: + name: page + in: query + description: Page number to retrieve. + schema: + type: integer + default: 1 + example: 2 + PaginationLimit: + name: limit + in: query + description: Number of days per page. + schema: + type: integer + default: 10 + maximum: 30 + example: 15 + EventId: + name: eventId + in: path + description: Identifier for a special event. + required: true + schema: + type: string + format: uuid + example: dad4bce8-f5cb-4078-a211-995864315e39 + StartDate: + name: startDate + in: query + description: >- + Starting date to retrieve future operating hours from. Defaults to + today's date. + schema: + type: string + format: date + example: '2023-02-23' + EndDate: + name: endDate + in: query + description: >- + End of a date range to retrieve special events for. Defaults to 7 days + after `startDate`. + schema: + type: string + format: date + example: '2023-04-18' + TicketId: + name: ticketId + in: path + description: >- + Identifier for a ticket to a museum event. Used to generate ticket + image. + required: true + schema: + type: string + format: uuid + example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c + responses: + BadRequest: + description: Bad request. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Not found. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Unauthorized. + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Error' +tags: + - name: Operations + description: Operational information about the museum. + - name: Events + description: Special events hosted by the museum. + - name: Tickets + description: Museum tickets for general entrance or special events. +security: + - MuseumPlaceholderAuth: [] diff --git a/resources/museum-tickets.yaml b/resources/museum-tickets.yaml new file mode 100644 index 0000000000..c0dd01ecf2 --- /dev/null +++ b/resources/museum-tickets.yaml @@ -0,0 +1,39 @@ +arazzo: 1.0.1 +info: + title: Redocly Museum API Tickets + description: >- + A part of imaginary, but delightful Museum API for interacting with museum services + and information. Built with love by Redocly. + version: 1.0.0 + +sourceDescriptions: + - name: museum-api + type: openapi + url: museum-api.yaml + +workflows: + - workflowId: get-museum-tickets + description: >- + This workflow demonstrates how to buy tickets for the museum. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: buy-tickets + description: >- + Buy museum tickets resolving request details with buyMuseumTickets operationId from museum-api.yaml description. + operationId: buyMuseumTickets + requestBody: + payload: + ticketType: general + ticketDate: 2023-09-07 + email: todd@example.com + successCriteria: + - condition: $statusCode == 201 + outputs: + ticketId: $response.body#/ticketId + fullBody: $response.body + outputs: + ticketId: $steps.buy-tickets.outputs.ticketId + stepFullBody: $steps.buy-tickets.outputs.fullBody diff --git a/scripts/local-pack.sh b/scripts/local-pack.sh index 9e2a56d971..1bb4e4c181 100755 --- a/scripts/local-pack.sh +++ b/scripts/local-pack.sh @@ -1,14 +1,39 @@ #!/bin/sh -# Backup package.json +# Backup package.json files +cp packages/core/package.json packages/core/package.json.bak +cp packages/respect-core/package.json packages/respect-core/package.json.bak cp packages/cli/package.json packages/cli/package.json.bak -cd packages/core && core=$(npm pack | tail -n 1) && mv $core ../../openapi-core.tgz && cd ../../ && +# Generate timestamp-based version +TIMESTAMP=$(date +%s) +VERSION="0.0.0-snapshot.$TIMESTAMP" -version=$(cat ./packages/core/package.json | jq '.version' | tr -d '"') -jq '.dependencies."@redocly/openapi-core" = $packagefile' ./packages/cli/package.json --arg packagefile ./openapi-core.tgz > package.json.tmp && mv package.json.tmp ./packages/cli/package.json && +# Build and pack core package +cd packages/core +jq ".version = \"$VERSION\"" package.json > tmp.json && mv tmp.json package.json +core=$(npm pack | tail -n 1) +mv $core ../../openapi-core.tgz +cd ../../ -cd packages/cli && cli=$(npm pack | tail -n 1) && mv $cli ../../redocly-cli.tgz +# Update and pack respect-core package +cd packages/respect-core +jq ".version = \"$VERSION\"" package.json > tmp.json && mv tmp.json package.json +jq ".dependencies[\"@redocly/openapi-core\"] = \"./openapi-core.tgz\"" package.json > tmp.json && mv tmp.json package.json +respect_core=$(npm pack | tail -n 1) +mv $respect_core ../../respect-core.tgz +cd ../../ -# Revert changes to the package.json -mv package.json.bak package.json +# Update and pack cli package +cd packages/cli +jq ".version = \"$VERSION\"" package.json > tmp.json && mv tmp.json package.json +jq ".dependencies[\"@redocly/openapi-core\"] = \"./openapi-core.tgz\"" package.json > tmp.json && mv tmp.json package.json +jq ".dependencies[\"@redocly/respect-core\"] = \"./respect-core.tgz\"" package.json > tmp.json && mv tmp.json package.json +cli=$(npm pack | tail -n 1) +mv $cli ../../redocly-cli.tgz +cd ../../ + +# Restore original package.json files +mv packages/core/package.json.bak packages/core/package.json +mv packages/respect-core/package.json.bak packages/respect-core/package.json +mv packages/cli/package.json.bak packages/cli/package.json diff --git a/scripts/post-changeset.js b/scripts/post-changeset.js index 9e85cf0318..ff8be7945f 100644 --- a/scripts/post-changeset.js +++ b/scripts/post-changeset.js @@ -2,8 +2,6 @@ const fs = require('fs'); const generatedLogsCli = fs.readFileSync('./packages/cli/CHANGELOG.md').toString(); const [, logCli] = generatedLogsCli.split('\n## ', 2); -const generatedLogsCore = fs.readFileSync('./packages/core/CHANGELOG.md').toString(); -const [, logCore] = generatedLogsCore.split('\n## ', 2); const mainChangelog = fs.readFileSync('./docs/changelog.md').toString(); const [date] = new Date().toISOString().split('T'); diff --git a/scripts/write-release-message.js b/scripts/write-release-message.js index ec7f3b82b8..ae00f6f65c 100644 --- a/scripts/write-release-message.js +++ b/scripts/write-release-message.js @@ -5,13 +5,17 @@ const generatedLogsCli = fs.readFileSync('./packages/cli/CHANGELOG.md').toString const [, logCli] = generatedLogsCli.split('\n## ', 2); const generatedLogsCore = fs.readFileSync('./packages/core/CHANGELOG.md').toString(); const [, logCore] = generatedLogsCore.split('\n## ', 2); +const generatedLogsRespectCore = fs.readFileSync('./packages/respect-core/CHANGELOG.md').toString(); +const [, logRespectCore] = generatedLogsRespectCore.split('\n## ', 2); fs.mkdirSync('./output', { recursive: true }); fs.writeFileSync( './output/release-message.json', JSON.stringify({ text: slackifyMarkdown( - `:bookmark: New @redocly/cli release ${logCli}\n\n:bookmark: New @redocly/openapi-core release ${logCore}\n\n` + `:bookmark: New @redocly/cli release ${logCli}\n\n` + + `:bookmark: New @redocly/openapi-core release ${logCore}\n\n` + + `:bookmark: New @redocly/respect-core release ${logRespectCore}\n\n` ), }) ); diff --git a/tsconfig.build.json b/tsconfig.build.json index 8a4d587a9d..0450f9022a 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,4 +1,8 @@ { "files": [], - "references": [{ "path": "packages/core" }, { "path": "packages/cli" }] + "references": [ + { "path": "packages/core" }, + { "path": "packages/cli" }, + { "path": "packages/respect-core" } + ] } diff --git a/tsconfig.json b/tsconfig.json index fb5fb64d6d..413e7c8503 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "composite": true, "declaration": true, "module": "commonjs", - "target": "ES2020", + "target": "ES2021", "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true, @@ -13,7 +13,7 @@ "strictFunctionTypes": true, "forceConsistentCasingInFileNames": true, "allowJs": false, - "lib": ["es2020", "es2020.string", "dom"], + "lib": ["ES2021", "ES2021.String", "DOM"], "baseUrl": "./packages", "skipLibCheck": true }