Skip to content

fix: pass required array into merge schema#797

Merged
kanadgupta merged 8 commits intomainfrom
fix/add-required-label-to-polymorphism
Sep 22, 2023
Merged

fix: pass required array into merge schema#797
kanadgupta merged 8 commits intomainfrom
fix/add-required-label-to-polymorphism

Conversation

@darrenyong
Copy link
Copy Markdown
Contributor

🚥 Resolves RM-8014

🧰 Changes

Currently, when we merge schemas, we don't pass in the required array. This makes is so that when an OAS file like the following comes up, the required array is cast out and there aren't any required parameters.

Nium — allOf with top-level oneOf
{
  "openapi": "3.0.1",
  "info": {
    "contact": {
      "email": "experience@nium.com",
      "name": "NIUM Platform",
      "url": "https://www.nium.com"
    },
    "description": "NIUM Platform",
    "license": {
      "name": "Copyright (c) 2023 NIUM"
    },
    "title": "FX",
    "version": "1.0"
  },
  "servers": [
    {
      "url": "https://gateway.nium.com/n1"
    }
  ],
  "paths": {
    "/api/v1/client/{clientHashId}/quotes": {
      "post": {
        "tags": [
          "Quotes"
        ],
        "x-info": {
          "apiType": "external"
        },
        "summary": "Create Quote",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteCreationResponse"
                },
                "examples": {
                  "With_Amount": {
                    "value": {
                      "id": "quote_6wBIHIRhPElAHfcgVaDFZs",
                      "quoteType": "balance_transfer",
                      "expiryTime": "2023-06-16 05:07:03",
                      "lockPeriod": "5_mins",
                      "markupRate": 0.0093611,
                      "createdTime": "2023-06-16 05:02:03",
                      "exchangeRate": 1.3373,
                      "sourceAmount": 100,
                      "netExchangeRate": 1.3279389,
                      "destinationAmount": 132.79,
                      "conversionSchedule": "immediate",
                      "sourceCurrencyCode\"": "USD",
                      "destinationCurrencyCode": "SGD",
                      "destinationMarkupAmount": 0.94
                    },
                    "summary": "With Amounts"
                  },
                  "With_ECB_Rate": {
                    "value": {
                      "id": "quote_6wBIHIRhPElAHfcgVaDFZs",
                      "quoteType": "balance_transfer",
                      "expiryTime": "2023-06-16 05:07:03",
                      "lockPeriod": "5_mins",
                      "markupRate": 0.0063973,
                      "createdTime": "2023-06-16 05:02:03",
                      "exchangeRate": 0.9139,
                      "sourceAmount": 100,
                      "ecbExchangeRate": 0.9139,
                      "netExchangeRate": 0.9075027,
                      "destinationAmount": 90.75,
                      "conversionSchedule": "immediate",
                      "sourceCurrencyCode\"": "EUR",
                      "destinationCurrencyCode": "SGD",
                      "destinationMarkupAmount": 0.64
                    },
                    "summary": "With ECB Rate"
                  },
                  "With_No_Amount": {
                    "value": {
                      "id": "quote_6wBIHIRhPElAHfcgVaDFZs",
                      "quoteType": "balance_transfer",
                      "expiryTime": "2023-06-16 05:07:03",
                      "lockPeriod": "5_mins",
                      "markupRate": 0.0093611,
                      "createdTime": "2023-06-16 05:02:03",
                      "exchangeRate": 1.3373,
                      "sourceAmount": null,
                      "netExchangeRate": 1.3279389,
                      "destinationAmount": null,
                      "conversionSchedule": "immediate",
                      "sourceCurrencyCode\"": "USD",
                      "destinationCurrencyCode": "SGD",
                      "destinationMarkupAmount": null
                    },
                    "summary": "With No Amount"
                  }
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "deprecated": false,
        "parameters": [
          {
            "$ref": "#/components/parameters/Authorization"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          },
          {
            "$ref": "#/components/parameters/ClientHashId"
          },
          {
            "description": "Enter a unique UUID value",
            "in": "header",
            "name": "x-request-id",
            "schema": {
              "type": "string",
              "example": "{{$guid}}"
            }
          }
        ],
        "description": "This API creates an FX quote for a currency pair according to the desired lock period and conversion schedule. The FX rate provided by this API includes the Nium markup and can be utilized for any FX conversion within the quote's validity period.",
        "operationId": "createQuote",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteCreationRequest"
              }
            }
          },
          "required": true,
          "description": "quoteCreationRequest"
        },
        "security": [
          {
            "default": []
          }
        ],
        "x-auth-type": "Application & Application User",
        "x-throttling-tier": "Unlimited",
        "x-codegen-request-body-name": "QuoteCreationRequest",
        "x-wso2-application-security": {
          "optional": false,
          "security-types": [
            "api_key"
          ]
        }
      }
    }
  },
  "components": {
    "schemas": {
      "DCC": {
        "type": "string",
        "example": "SGD",
        "maxLength": 3,
        "minLength": 3,
        "description": "3-letter [ISO-4217 currency code](https://www.iso.org/iso-4217-currency-codes.html) for the destination amount."
      },
      "SCC": {
        "type": "string",
        "example": "USD",
        "maxLength": 3,
        "minLength": 3,
        "description": "3-letter [ISO-4217 currency code](https://www.iso.org/iso-4217-currency-codes.html) for the source amount.",
        "required": [
          "sourceCurrencyCode"
        ]
      },
      "QuoteId": {
        "type": "string",
        "example": "quote_1234567890abcdefABCDEF",
        "description": "Unique identifier of the quote."
      },
      "QuoteType": {
        "enum": [
          "balance_transfer",
          "payout"
        ],
        "type": "string",
        "title": "QuoteType",
        "description": "The type of the quote."
      },
      "LockPeriod": {
        "enum": [
          "5_mins",
          "15_mins",
          "1_hour",
          "4_hours",
          "8_hours",
          "24_hours"
        ],
        "type": "string",
        "title": "LockPeriod",
        "default": "5_mins",
        "example": "15_mins",
        "description": "The time period for which the quote is locked."
      },
      "MarkupRate": {
        "type": "number",
        "format": "double",
        "example": 0.006745677,
        "description": "Markup rate applied by Nium."
      },
      "CreatedTime": {
        "type": "string",
        "format": "date-time",
        "example": "2021-03-09T06:46:03Z",
        "description": "Time of creation in UTC."
      },
      "ExchangeRate": {
        "type": "number",
        "format": "double",
        "example": 1.349324513,
        "description": "Foreign exchange market rate for the currency pair, used as the benchmark for quote calculation."
      },
      "CoreApiHashId": {
        "type": "string",
        "example": "abc1234-5d6e-0a8b-c8d7-3a7706a0c312c",
        "maxLength": 36,
        "minLength": 36
      },
      "ErrorCodes400": {
        "enum": [
          "fx_constraint_violated_input",
          "fx_invalid_format_input",
          "fx_invalid_currency_code",
          "fx_invalid_currency_amount",
          "fx_missing_input",
          "fx_quote_expired",
          "fx_insufficient_balance",
          "fx_transfer_status_invalid",
          "fx_locked_period"
        ],
        "type": "string",
        "description": "The detailed error code associated with HTTP status 400.\n\n* `fx_constraint_violated_input`: The input violates the model constraints.\n* `fx_invalid_format_input`: The input format is invalid.\n* `fx_invalid_currency_code`: The input currency code is invalid.\n* `fx_invalid_currency_amount`: The input currency amount is invalid.\n* `fx_missing_input`: The required fields are missing.\n* `fx_quote_expired`: The provided quote is expired.\n* `fx_insufficient_balance`: The balance in the account is insufficient to complete/schedule the transfer.\n* `fx_transfer_status_invalid`: The transfer status is invalid.\n* `fx_locked_period`: The requested conversion is in locked period.\n"
      },
      "ErrorCodes401": {
        "enum": [
          "fx_client_unauthenticated"
        ],
        "type": "string",
        "description": "The detailed error code associated with HTTP status 401.\n* `fx_client_unauthenticated`: The client request lacks valid authentication credentials.\n"
      },
      "ErrorCodes403": {
        "enum": [
          "fx_client_no_access"
        ],
        "type": "string",
        "description": "The detailed error code associated with HTTP status 403.\n* `fx_client_no_access`: The client is authenticated but not authorized.\n"
      },
      "ErrorCodes404": {
        "enum": [
          "fx_quote_not_found",
          "fx_fee_configuration_not_found"
        ],
        "type": "string",
        "description": "The detailed error code associated with HTTP status 404.\n* `fx_quote_not_found`: The provided quote cannot be found in the service.\n* `fx_fee_configuration_not_found`: The configuration cannot be found for at least one of the fees.\n"
      },
      "ErrorCodes500": {
        "enum": [
          "fx_dependency_error",
          "fx_uncategorized_error"
        ],
        "type": "string",
        "description": "The detailed error code associated with HTTP status 500.\n* `fx_dependency_error`: Error happens when the service calls its dependencies.\n* `fx_uncategorized_error`: Service errors not categorized.\n"
      },
      "ErrorDetail400": {
        "type": "object",
        "properties": {
          "code": {
            "$ref": "#/components/schemas/ErrorCodes400"
          },
          "description": {
            "type": "string",
            "description": "Description of the error."
          }
        },
        "description": "error details description"
      },
      "ErrorDetail401": {
        "type": "object",
        "properties": {
          "code": {
            "$ref": "#/components/schemas/ErrorCodes401"
          },
          "description": {
            "type": "string",
            "description": "Description of the error."
          }
        },
        "description": "error details description"
      },
      "ErrorDetail403": {
        "type": "object",
        "properties": {
          "code": {
            "$ref": "#/components/schemas/ErrorCodes403"
          },
          "description": {
            "type": "string",
            "description": "Description of the error."
          }
        },
        "description": "error details description"
      },
      "ErrorDetail404": {
        "type": "object",
        "properties": {
          "code": {
            "$ref": "#/components/schemas/ErrorCodes404"
          },
          "description": {
            "type": "string",
            "description": "Description of the error."
          }
        },
        "description": "error details description"
      },
      "ErrorDetail500": {
        "type": "object",
        "properties": {
          "code": {
            "$ref": "#/components/schemas/ErrorCodes500"
          },
          "description": {
            "type": "string",
            "description": "Description of the error."
          }
        },
        "description": "error details description"
      },
      "EcbExchangeRate": {
        "type": "number",
        "format": "double",
        "example": 1.349324513,
        "description": "Europe Central Bank's exchange rate for this currency pair, provided only for EU and UK.",
        "x-field-extra-annotation": "@JsonInclude(JsonInclude.Include.NON_NULL)"
      },
      "NetExchangeRate": {
        "type": "number",
        "format": "double",
        "example": 1.342255231,
        "description": "Exchange rate with markup to be used for the conversion."
      },
      "QuoteExpiryTime": {
        "type": "string",
        "format": "date-time",
        "example": "2021-03-09T06:46:03Z",
        "description": "Expiry time of the quote in UTC."
      },
      "ErrorResponse400": {
        "type": "object",
        "properties": {
          "errorDetails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorDetail400"
            }
          }
        }
      },
      "ErrorResponse401": {
        "type": "object",
        "properties": {
          "errorDetails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorDetail401"
            }
          }
        }
      },
      "ErrorResponse403": {
        "type": "object",
        "properties": {
          "errorDetails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorDetail403"
            }
          }
        }
      },
      "ErrorResponse404": {
        "type": "object",
        "properties": {
          "errorDetails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorDetail404"
            }
          }
        }
      },
      "ErrorResponse500": {
        "type": "object",
        "properties": {
          "errorDetails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorDetail500"
            }
          }
        }
      },
      "ConversionSchedule": {
        "enum": [
          "immediate",
          "end_of_day",
          "next_day",
          "2_days"
        ],
        "type": "string",
        "title": "ConversionSchedule",
        "default": "immediate",
        "example": "immediate",
        "description": "The time period after which the conversion should be settled."
      },
      "SourceAmount_Quote": {
        "type": "number",
        "format": "double",
        "example": 13.42,
        "minimum": 0,
        "nullable": true,
        "description": "The source amount to be converted to the destination currency. This value is for reference only and will not be used as the actual conversion amount.",
        "exclusiveMinimum": true
      },
      "QuoteCreationRequest": {
        "allOf": [
          {
            "type": "object",
            "properties": {
              "quoteType": {
                "$ref": "#/components/schemas/QuoteType"
              },
              "lockPeriod": {
                "$ref": "#/components/schemas/LockPeriod"
              },
              "conversionSchedule": {
                "$ref": "#/components/schemas/ConversionSchedule"
              },
              "sourceCurrencyCode": {
                "$ref": "#/components/schemas/SCC"
              },
              "destinationCurrencyCode": {
                "$ref": "#/components/schemas/DCC"
              }
            }
          },
          {
            "$ref": "#/components/schemas/EitherSourceOrDestinationAmountOrNoAmount"
          }
        ],
        "required": [
          "sourceCurrencyCode",
          "destinationCurrencyCode",
          "quoteType"
        ]
      },
      "QuoteCreationResponse": {
        "type": "object",
        "title": "QuoteCreationResponse",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/QuoteId"
          },
          "quoteType": {
            "$ref": "#/components/schemas/QuoteType"
          },
          "expiryTime": {
            "$ref": "#/components/schemas/QuoteExpiryTime"
          },
          "lockPeriod": {
            "$ref": "#/components/schemas/LockPeriod"
          },
          "markupRate": {
            "$ref": "#/components/schemas/MarkupRate"
          },
          "createdTime": {
            "$ref": "#/components/schemas/CreatedTime"
          },
          "exchangeRate": {
            "$ref": "#/components/schemas/ExchangeRate"
          },
          "sourceAmount": {
            "$ref": "#/components/schemas/SourceAmount_Quote"
          },
          "ecbExchangeRate": {
            "$ref": "#/components/schemas/EcbExchangeRate"
          },
          "netExchangeRate": {
            "$ref": "#/components/schemas/NetExchangeRate"
          },
          "destinationAmount": {
            "$ref": "#/components/schemas/DestinationAmount_Quote"
          },
          "conversionSchedule": {
            "$ref": "#/components/schemas/ConversionSchedule"
          },
          "sourceCurrencyCode": {
            "$ref": "#/components/schemas/SCC"
          },
          "destinationCurrencyCode": {
            "$ref": "#/components/schemas/DCC"
          },
          "destinationMarkupAmount": {
            "$ref": "#/components/schemas/DestinationMarkupAmount"
          }
        }
      },
      "DestinationAmount_Quote": {
        "type": "number",
        "format": "double",
        "example": 13.42,
        "minimum": 0,
        "nullable": true,
        "description": "The amount needed in the destination currency. This value is for reference only and will not be used as the actual conversion amount.",
        "exclusiveMinimum": true
      },
      "DestinationMarkupAmount": {
        "type": "number",
        "format": "double",
        "example": 0.07,
        "nullable": true,
        "description": "The markup amount in the destination currency."
      },
      "EitherSourceOrDestinationAmountOrNoAmount": {
        "oneOf": [
          {
            "type": "object",
            "title": "With No Amount"
          },
          {
            "type": "object",
            "title": "With Source Amount",
            "properties": {
              "sourceAmount": {
                "$ref": "#/components/schemas/SourceAmount_Quote"
              }
            }
          },
          {
            "type": "object",
            "title": "With Destination Amount",
            "properties": {
              "destinationAmount": {
                "$ref": "#/components/schemas/DestinationAmount_Quote"
              }
            }
          }
        ]
      }
    },
    "responses": {
      "NotFound": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse404"
            }
          }
        },
        "description": "Error response when the requested resource cannot be found."
      },
      "Forbidden": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse403"
            }
          }
        },
        "description": "Error response when the requested resource is forbidden."
      },
      "BadRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse400"
            }
          }
        },
        "description": "Error response when the request format is invalid."
      },
      "Unauthorized": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse401"
            }
          }
        },
        "description": "Error response when the request is unauthorized."
      },
      "InternalError": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse500"
            }
          }
        },
        "description": "Error response when service has internal error."
      }
    },
    "parameters": {
      "CsrfToken": {
        "in": "header",
        "name": "csrf_token",
        "schema": {
          "type": "string"
        },
        "required": false,
        "x-internal": true
      },
      "ClientHashId": {
        "in": "path",
        "name": "clientHashId",
        "schema": {
          "$ref": "#/components/schemas/CoreApiHashId"
        },
        "required": true,
        "description": "Unique identifier of the client."
      },
      "Authorization": {
        "in": "header",
        "name": "Authorization",
        "schema": {
          "type": "string"
        },
        "required": false,
        "x-internal": true
      }
    },
    "securitySchemes": {
      "default": {
        "in": "header",
        "name": "x-api-key",
        "type": "apiKey"
      }
    }
  },
  "security": [
    {
      "default": []
    }
  ],
  "tags": [
    {
      "description": "The Quotes API",
      "name": "Quotes"
    }
  ],
  "x-explorer-enabled": true,
  "x-proxy-enabled": true,
  "x-samples-enabled": true,
  "x-samples-languages": [
    "curl",
    "python",
    "java",
    "node",
    "ruby",
    "php",
    "c",
    "cplusplus",
    "csharp",
    "go"
  ],
  "x-wso2-application-security": {
    "optional": false,
    "security-types": [
      "api_key"
    ]
  },
  "x-wso2-basePath": "/baas/1.0",
  "x-wso2-cors": {
    "accessControlAllowCredentials": false,
    "accessControlAllowHeaders": [
      "authorization",
      "Access-Control-Allow-Origin",
      "Content-Type",
      "SOAPAction",
      "testKey",
      "x-request-id",
      "x-client-name",
      "apiKey",
      "x-api-key"
    ],
    "accessControlAllowMethods": [
      "GET",
      "PUT",
      "POST",
      "DELETE",
      "PATCH",
      "OPTIONS"
    ],
    "accessControlAllowOrigins": [
      "*"
    ],
    "corsConfigurationEnabled": true
  },
  "x-wso2-production-endpoints": {
    "type": "default"
  },
  "x-wso2-response-cache": {
    "cacheTimeoutInSeconds": 300,
    "enabled": false
  },
  "x-wso2-sandbox-endpoints": {
    "type": "default"
  },
  "x-wso2-transports": [
    "http",
    "https"
  ]
}

🧬 QA & Testing

No way to test this aside from that one test I wrote.

Copy link
Copy Markdown
Contributor

@kanadgupta kanadgupta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

100+ lines of test changes, a single line fix... love PRs like these 🫶🏽

@darrenyong
Copy link
Copy Markdown
Contributor Author

@kanadgupta
noticed I was getting the following linter error on one of my tests

Property 'required' does not exist on type 'boolean

so switched it to use .toHaveProperty instead of .toBeTruthy. it's a more explicit test too since I laid out exactly what the required array should contain rather than it just existing!

@kanadgupta kanadgupta merged commit 7aff6fc into main Sep 22, 2023
@kanadgupta kanadgupta deleted the fix/add-required-label-to-polymorphism branch September 22, 2023 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants