diff --git a/README.md b/README.md index 722f89c4..c59e90cc 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ A wide variety of refactoring processes are run on the raw schemas to get them r * **Combine raw schemas:** If a given API segment has multiple OpenAPI model files, they are combined into a single file. Routes, methods, and component schemas are aggregated across original OpenAPI files, and combined into a single model object. So far, we have not found any OpenAPI model files from the same API segment with overlapping routes, nor conflicting component schemas, so we're not handling those cases. * **Clean combined schema:** There are often inconsistencies, missing elements, and duplicated or unnecessary component schemas in the OpenAPI models, so this step takes care of those issues. Some specific current steps that are taken: - * Filling in missing `404` response schemas + * Filling in missing `404` and `429` response schemas * Removing unnecessary `allOf` and `oneOf` definitions in requests, responses, and component schemas * Setting the types of schemas and properties that are missing explicit types * Flattening multi-level `$ref`s, where a component or property is a `$ref` to another `$ref`. diff --git a/composer.json b/composer.json index 2b9611ed..76c866b0 100644 --- a/composer.json +++ b/composer.json @@ -9,13 +9,14 @@ ], "license": "Apache-2.0", "require": { - "saloonphp/saloon": "^3.8" + "saloonphp/saloon": "^3.8", + "saloonphp/rate-limit-plugin": "^2.1" }, "require-dev": { "highsidelabs/saloon-sdk-generator": "^2.1", "psy/psysh": "^0.12.3", "symfony/console": "^7.0", - "phpcompatibility/php-compatibility": "^9.3", + "phpcompatibility/php-compatibility": "dev-develop", "laravel/pint": "^1.15", "composer/semver": "^3.4", "ramsey/uuid": "^4.7", diff --git a/resources/metadata/modifications.json b/resources/metadata/modifications.json index 9d460253..31bb6cbb 100644 --- a/resources/metadata/modifications.json +++ b/resources/metadata/modifications.json @@ -36,6 +36,14 @@ "value": { "type": "string" } + }, + { + "comment": "Set type of InvoiceDetails.amount to a string as it requires trailing zeros", + "action": "merge", + "path": "components.schemas.InvoiceDetails.properties.amount", + "value": { + "type": "string" + } } ] }, diff --git a/resources/models/account-registration/v1.json b/resources/models/account-registration/v1.json index c6fc5685..4582a29f 100644 --- a/resources/models/account-registration/v1.json +++ b/resources/models/account-registration/v1.json @@ -142,6 +142,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -365,6 +375,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO_2" + } + } + } } }, "x-code-samples": [ @@ -546,6 +566,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO_3" + } + } + } } }, "x-code-samples": [ @@ -767,6 +797,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO_3" + } + } + } } }, "x-code-samples": [ @@ -1107,7 +1147,7 @@ "example": "2022-10-09" }, "amount": { - "type": "number", + "type": "string", "description": "Used to validate Invoice details whether invoice is related to user or not. It accepts upto 2 digit of decimal.", "example": 2323.22 } diff --git a/resources/models/address-validation/v1.json b/resources/models/address-validation/v1.json index 0365f437..cca19ace 100644 --- a/resources/models/address-validation/v1.json +++ b/resources/models/address-validation/v1.json @@ -142,6 +142,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ diff --git a/resources/models/authorization/v1.json b/resources/models/authorization/v1.json index 2c14c277..2d4e08b5 100644 --- a/resources/models/authorization/v1.json +++ b/resources/models/authorization/v1.json @@ -123,6 +123,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ diff --git a/resources/models/consolidation/v1.json b/resources/models/consolidation/v1.json index 0687834f..57d96db8 100644 --- a/resources/models/consolidation/v1.json +++ b/resources/models/consolidation/v1.json @@ -181,6 +181,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ @@ -397,6 +407,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "tags": [ @@ -585,6 +605,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ @@ -803,6 +833,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ @@ -1021,6 +1061,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ @@ -1239,6 +1289,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ @@ -1457,6 +1517,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ @@ -1648,6 +1718,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO_6" + } + } + } } }, "x-code-samples": [ @@ -1866,6 +1946,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ diff --git a/resources/models/freight-ltl/v1.json b/resources/models/freight-ltl/v1.json index 5444e1c6..dda892bb 100644 --- a/resources/models/freight-ltl/v1.json +++ b/resources/models/freight-ltl/v1.json @@ -134,6 +134,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -337,6 +347,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -549,6 +569,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -752,6 +782,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -955,6 +995,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ diff --git a/resources/models/global-trade/v1.json b/resources/models/global-trade/v1.json index bf0183fe..05841000 100644 --- a/resources/models/global-trade/v1.json +++ b/resources/models/global-trade/v1.json @@ -84,6 +84,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ diff --git a/resources/models/ground-eod-close/v1.json b/resources/models/ground-eod-close/v1.json index 74e7d659..f7ad6ae3 100644 --- a/resources/models/ground-eod-close/v1.json +++ b/resources/models/ground-eod-close/v1.json @@ -140,6 +140,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -357,6 +367,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ diff --git a/resources/models/locations-search/v1.json b/resources/models/locations-search/v1.json index fd3ac164..214e213e 100644 --- a/resources/models/locations-search/v1.json +++ b/resources/models/locations-search/v1.json @@ -182,6 +182,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ diff --git a/resources/models/open-ship/v1.json b/resources/models/open-ship/v1.json index bd3677d0..e1d6ac29 100644 --- a/resources/models/open-ship/v1.json +++ b/resources/models/open-ship/v1.json @@ -124,6 +124,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -343,6 +353,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -561,6 +581,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -751,6 +781,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -969,6 +1009,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -1159,6 +1209,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -1379,6 +1439,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -1599,6 +1669,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -1799,6 +1879,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -2019,6 +2109,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ diff --git a/resources/models/pickup-request/v1.json b/resources/models/pickup-request/v1.json index 8386db72..b17168f2 100644 --- a/resources/models/pickup-request/v1.json +++ b/resources/models/pickup-request/v1.json @@ -141,6 +141,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -361,6 +371,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -581,6 +601,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ diff --git a/resources/models/postal-code-validation/v1.json b/resources/models/postal-code-validation/v1.json index cd1a5fd6..4ed2b10c 100644 --- a/resources/models/postal-code-validation/v1.json +++ b/resources/models/postal-code-validation/v1.json @@ -141,6 +141,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ diff --git a/resources/models/rates-transit-times/v1.json b/resources/models/rates-transit-times/v1.json index 232fcc5b..6aeb0aea 100644 --- a/resources/models/rates-transit-times/v1.json +++ b/resources/models/rates-transit-times/v1.json @@ -141,6 +141,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ diff --git a/resources/models/service-availability/v1.json b/resources/models/service-availability/v1.json index f0a0f533..2bbeaf6c 100644 --- a/resources/models/service-availability/v1.json +++ b/resources/models/service-availability/v1.json @@ -155,6 +155,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ @@ -333,6 +343,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO_2" + } + } + } } }, "parameters": [ @@ -553,6 +573,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ diff --git a/resources/models/ship-dg-hazmat/v1.json b/resources/models/ship-dg-hazmat/v1.json index 462cc8e5..723e27aa 100644 --- a/resources/models/ship-dg-hazmat/v1.json +++ b/resources/models/ship-dg-hazmat/v1.json @@ -176,6 +176,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ @@ -394,6 +404,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO_2" + } + } + } } }, "x-code-samples": [ @@ -610,6 +630,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ diff --git a/resources/models/ship/v1.json b/resources/models/ship/v1.json index 4699c860..d33c32dd 100644 --- a/resources/models/ship/v1.json +++ b/resources/models/ship/v1.json @@ -141,6 +141,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -361,6 +371,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -625,6 +645,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ @@ -767,6 +797,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -987,6 +1027,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -1258,6 +1308,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "x-code-samples": [ diff --git a/resources/models/track/v1.json b/resources/models/track/v1.json index 372a7351..009de13d 100644 --- a/resources/models/track/v1.json +++ b/resources/models/track/v1.json @@ -141,6 +141,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -362,6 +372,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -583,6 +603,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -804,6 +834,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -1025,6 +1065,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ @@ -1246,6 +1296,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "parameters": [ diff --git a/resources/models/trade-documents-upload/v1.json b/resources/models/trade-documents-upload/v1.json index 2009be19..77647082 100644 --- a/resources/models/trade-documents-upload/v1.json +++ b/resources/models/trade-documents-upload/v1.json @@ -175,6 +175,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "servers": [ @@ -377,6 +387,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO" + } + } + } } }, "servers": [ @@ -637,6 +657,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO_2" + } + } + } } }, "x-code-samples": [ @@ -1019,6 +1049,16 @@ } } } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseVO_2" + } + } + } } }, "x-code-samples": [ diff --git a/src/Api/AccountRegistrationV1/Api.php b/src/Api/AccountRegistrationV1/Api.php index ae68054c..ecdc8ae1 100644 --- a/src/Api/AccountRegistrationV1/Api.php +++ b/src/Api/AccountRegistrationV1/Api.php @@ -33,25 +33,40 @@ public function createCustomerKey(CustomerKeyInputVo $customerKeyInputVo): Respo /** * @param ValidatePinInputVo $validatePinInputVo This field indicates INPUT model + * @param string $accountAuthToken Address validation jwt + * @param ?string $xClientid This indicates the Client who is consuming this endpoint */ - public function validatePin(ValidatePinInputVo $validatePinInputVo): Response - { - return $this->connector->send(new ValidatePin($validatePinInputVo)); + public function validatePin( + ValidatePinInputVo $validatePinInputVo, + string $accountAuthToken, + ?string $xClientid = null, + ): Response { + return $this->connector->send(new ValidatePin($validatePinInputVo, $accountAuthToken, $xClientid)); } /** * @param ValidateInvoiceInputVo $validateInvoiceInputVo This field indicates Invoice Information ( Invoice number, Date, currency and amount) and locale. + * @param string $accountAuthToken Address validation jwt + * @param ?string $xClientid This indicates the Client who is consuming this endpoint */ - public function validateInvoice(ValidateInvoiceInputVo $validateInvoiceInputVo): Response - { - return $this->connector->send(new ValidateInvoice($validateInvoiceInputVo)); + public function validateInvoice( + ValidateInvoiceInputVo $validateInvoiceInputVo, + string $accountAuthToken, + ?string $xClientid = null, + ): Response { + return $this->connector->send(new ValidateInvoice($validateInvoiceInputVo, $accountAuthToken, $xClientid)); } /** * @param IssuePinInputVo $issuePinInputVo This field indicates INPUT model + * @param string $accountAuthToken Address validation jwt + * @param ?string $xClientid This indicates the client who is using this endpoint. */ - public function sendPin(IssuePinInputVo $issuePinInputVo): Response - { - return $this->connector->send(new SendPin($issuePinInputVo)); + public function sendPin( + IssuePinInputVo $issuePinInputVo, + string $accountAuthToken, + ?string $xClientid = null, + ): Response { + return $this->connector->send(new SendPin($issuePinInputVo, $accountAuthToken, $xClientid)); } } diff --git a/src/Api/AccountRegistrationV1/Dto/InvoiceDetails.php b/src/Api/AccountRegistrationV1/Dto/InvoiceDetails.php index 60f19309..4726b045 100644 --- a/src/Api/AccountRegistrationV1/Dto/InvoiceDetails.php +++ b/src/Api/AccountRegistrationV1/Dto/InvoiceDetails.php @@ -18,12 +18,12 @@ final class InvoiceDetails extends Dto * @param int $number Used to validate invoice details .It accepts no decimal. * @param string $currency Used to validate Invoice details whether invoice is related to user or not.It accepts upto 4 digits currency.
Click here to see Currency Codes * @param string $date Used to validate Invoice details whether invoice is related to user or not. Date format is YYYY-MM-DD. - * @param float $amount Used to validate Invoice details whether invoice is related to user or not. It accepts upto 2 digit of decimal. + * @param string $amount Used to validate Invoice details whether invoice is related to user or not. It accepts upto 2 digit of decimal. */ public function __construct( public int $number, public string $currency, public string $date, - public float $amount, + public string $amount, ) {} } diff --git a/src/Api/AccountRegistrationV1/Requests/CreateCustomerKey.php b/src/Api/AccountRegistrationV1/Requests/CreateCustomerKey.php index 494e78fd..e55b9bf6 100644 --- a/src/Api/AccountRegistrationV1/Requests/CreateCustomerKey.php +++ b/src/Api/AccountRegistrationV1/Requests/CreateCustomerKey.php @@ -50,7 +50,7 @@ public function createDtoFromResponse(Response $response): IrcpResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => IrcpResponseVo::class, - 400, 401, 403, 404, 500 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/AccountRegistrationV1/Requests/SendPin.php b/src/Api/AccountRegistrationV1/Requests/SendPin.php index 27c01ffa..0ab6081a 100644 --- a/src/Api/AccountRegistrationV1/Requests/SendPin.php +++ b/src/Api/AccountRegistrationV1/Requests/SendPin.php @@ -54,7 +54,7 @@ public function createDtoFromResponse(Response $response): PinGenerationOutputVo $status = $response->status(); $responseCls = match ($status) { 200 => PinGenerationOutputVo::class, - 400, 401, 403, 404, 500 => ErrorResponseVo3::class, + 400, 401, 403, 404, 500, 429 => ErrorResponseVo3::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/AccountRegistrationV1/Requests/ValidateInvoice.php b/src/Api/AccountRegistrationV1/Requests/ValidateInvoice.php index 579398f2..f627f8bc 100644 --- a/src/Api/AccountRegistrationV1/Requests/ValidateInvoice.php +++ b/src/Api/AccountRegistrationV1/Requests/ValidateInvoice.php @@ -55,7 +55,7 @@ public function createDtoFromResponse(Response $response): IrcpResponseVo2|Error $status = $response->status(); $responseCls = match ($status) { 200 => IrcpResponseVo2::class, - 400, 404, 500 => ErrorResponseVo3::class, + 400, 404, 500, 429 => ErrorResponseVo3::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/AccountRegistrationV1/Requests/ValidatePin.php b/src/Api/AccountRegistrationV1/Requests/ValidatePin.php index 07d43322..f368f7fb 100644 --- a/src/Api/AccountRegistrationV1/Requests/ValidatePin.php +++ b/src/Api/AccountRegistrationV1/Requests/ValidatePin.php @@ -54,7 +54,7 @@ public function createDtoFromResponse(Response $response): IrcpResponseVo2|Error $status = $response->status(); $responseCls = match ($status) { 200 => IrcpResponseVo2::class, - 400, 404, 500 => ErrorResponseVo2::class, + 400, 404, 500, 429 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/AddressValidationV1/Requests/ValidateAddress.php b/src/Api/AddressValidationV1/Requests/ValidateAddress.php index 2953376b..a5365f4d 100644 --- a/src/Api/AddressValidationV1/Requests/ValidateAddress.php +++ b/src/Api/AddressValidationV1/Requests/ValidateAddress.php @@ -52,7 +52,7 @@ public function createDtoFromResponse(Response $response): AdvcResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => AdvcResponseVo::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php index a7194cdf..4577a726 100644 --- a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php +++ b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php @@ -11,13 +11,22 @@ namespace ShipStream\FedEx\Api\AuthorizationV1\Requests; use Exception; +use Saloon\Contracts\Authenticator; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; +use Saloon\Helpers\URLHelper; +use Saloon\Http\Auth\NullAuthenticator; +use Saloon\Http\PendingRequest; use Saloon\Http\Response as Response1; +use Saloon\RateLimitPlugin\Contracts\RateLimitStore; +use Saloon\RateLimitPlugin\Limit; +use Saloon\RateLimitPlugin\Stores\MemoryStore; +use Saloon\RateLimitPlugin\Traits\HasRateLimits; use Saloon\Traits\Body\HasFormBody; use ShipStream\FedEx\Api\AuthorizationV1\Dto\FullSchema; use ShipStream\FedEx\Api\AuthorizationV1\Responses\ErrorResponseVo; use ShipStream\FedEx\Api\AuthorizationV1\Responses\Response; +use ShipStream\FedEx\Enums\Endpoint; use ShipStream\FedEx\Request; /** @@ -30,6 +39,7 @@ class ApiAuthorization extends Request implements HasBody { use HasFormBody; + use HasRateLimits; protected Method $method = Method::POST; @@ -38,7 +48,10 @@ class ApiAuthorization extends Request implements HasBody */ public function __construct( public FullSchema $fullSchema, - ) {} + ?RateLimitStore $rateLimitStore = null, + ) { + $this->rateLimitStore = $rateLimitStore; + } public function resolveEndpoint(): string { @@ -50,7 +63,7 @@ public function createDtoFromResponse(Response1 $response): Response|ErrorRespon $status = $response->status(); $responseCls = match ($status) { 200 => Response::class, - 401, 500, 503 => ErrorResponseVo::class, + 401, 500, 503, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; @@ -61,4 +74,31 @@ public function defaultBody(): array { return $this->fullSchema->toArray(); } + + public function resolveLimits(): array + { + return [ + Limit::allow(requests: 14)->everySeconds(5)->name('burst-threshold'), + Limit::allow(requests: 119)->everySeconds(120)->name('average-threshold'), + ]; + } + + public function resolveRateLimitStore(): RateLimitStore + { + return new MemoryStore; + } + + public function defaultAuth(): Authenticator + { + return new NullAuthenticator; + } + + public function boot(PendingRequest $pendingRequest): void + { + // Ensure that authorization always uses the correct base url so that connectors with a different base url can still authenticate + /** @var Endpoint $endpoint */ + if ($endpoint = ($pendingRequest->getConnector()->endpoint ?? null)) { + $pendingRequest->setUrl(URLHelper::join($endpoint->isProduction() ? Endpoint::PROD->value : Endpoint::SANDBOX->value, $pendingRequest->getRequest()->resolveEndpoint())); + } + } } diff --git a/src/Api/ConsolidationV1/Requests/ConfirmConsolidationResults.php b/src/Api/ConsolidationV1/Requests/ConfirmConsolidationResults.php index a21ac046..e91e6951 100644 --- a/src/Api/ConsolidationV1/Requests/ConfirmConsolidationResults.php +++ b/src/Api/ConsolidationV1/Requests/ConfirmConsolidationResults.php @@ -52,7 +52,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ConfirmConsolidationResultsResponse::class, - 400 => ErrorResponseVo::class, + 400, 429 => ErrorResponseVo::class, 401 => ErrorResponseVo2::class, 403 => ErrorResponseVo3::class, 404 => ErrorResponseVo4::class, diff --git a/src/Api/ConsolidationV1/Requests/ConfirmConsolidations.php b/src/Api/ConsolidationV1/Requests/ConfirmConsolidations.php index a34d007d..a2d2df5f 100644 --- a/src/Api/ConsolidationV1/Requests/ConfirmConsolidations.php +++ b/src/Api/ConsolidationV1/Requests/ConfirmConsolidations.php @@ -52,7 +52,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ConfirmConsolidationResponse::class, - 400 => ErrorResponseVo::class, + 400, 429 => ErrorResponseVo::class, 401 => ErrorResponseVo2::class, 403 => ErrorResponseVo3::class, 404 => ErrorResponseVo4::class, diff --git a/src/Api/ConsolidationV1/Requests/ConsolidationShipmentResults.php b/src/Api/ConsolidationV1/Requests/ConsolidationShipmentResults.php index c5ca0a85..0a20d3d4 100644 --- a/src/Api/ConsolidationV1/Requests/ConsolidationShipmentResults.php +++ b/src/Api/ConsolidationV1/Requests/ConsolidationShipmentResults.php @@ -53,7 +53,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ConsolidationShipmentResultsResponse::class, - 400 => ErrorResponseVo::class, + 400, 429 => ErrorResponseVo::class, 401 => ErrorResponseVo2::class, 403 => ErrorResponseVo3::class, 404 => ErrorResponseVo4::class, diff --git a/src/Api/ConsolidationV1/Requests/CreateConsolidation.php b/src/Api/ConsolidationV1/Requests/CreateConsolidation.php index 029f7004..b7fea3ec 100644 --- a/src/Api/ConsolidationV1/Requests/CreateConsolidation.php +++ b/src/Api/ConsolidationV1/Requests/CreateConsolidation.php @@ -56,7 +56,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => CreateConsolidationResponse::class, - 400 => ErrorResponseVo::class, + 400, 429 => ErrorResponseVo::class, 401 => ErrorResponseVo2::class, 403 => ErrorResponseVo3::class, 404 => ErrorResponseVo4::class, diff --git a/src/Api/ConsolidationV1/Requests/CreateConsolidationShipment.php b/src/Api/ConsolidationV1/Requests/CreateConsolidationShipment.php index dd6a86d4..eb643320 100644 --- a/src/Api/ConsolidationV1/Requests/CreateConsolidationShipment.php +++ b/src/Api/ConsolidationV1/Requests/CreateConsolidationShipment.php @@ -55,7 +55,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => CreateConsolidationShipmentResponse::class, - 400 => ErrorResponseVo::class, + 400, 429 => ErrorResponseVo::class, 401 => ErrorResponseVo2::class, 403 => ErrorResponseVo3::class, 404 => ErrorResponseVo4::class, diff --git a/src/Api/ConsolidationV1/Requests/DeleteConsolidation.php b/src/Api/ConsolidationV1/Requests/DeleteConsolidation.php index 7e3fab83..393060d5 100644 --- a/src/Api/ConsolidationV1/Requests/DeleteConsolidation.php +++ b/src/Api/ConsolidationV1/Requests/DeleteConsolidation.php @@ -56,7 +56,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => DeleteConsolidationResponse::class, - 400 => ErrorResponseVo::class, + 400, 429 => ErrorResponseVo::class, 401 => ErrorResponseVo2::class, 403 => ErrorResponseVo3::class, 404 => ErrorResponseVo4::class, diff --git a/src/Api/ConsolidationV1/Requests/DeleteConsolidationShipments.php b/src/Api/ConsolidationV1/Requests/DeleteConsolidationShipments.php index f24d96b3..ed8e5946 100644 --- a/src/Api/ConsolidationV1/Requests/DeleteConsolidationShipments.php +++ b/src/Api/ConsolidationV1/Requests/DeleteConsolidationShipments.php @@ -50,7 +50,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVo::class, - 400, 401, 403, 500 => ErrorResponseVo6::class, + 400, 401, 403, 500, 429 => ErrorResponseVo6::class, 404 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ConsolidationV1/Requests/ModifyConsolidation.php b/src/Api/ConsolidationV1/Requests/ModifyConsolidation.php index c9f8f306..5b354a5f 100644 --- a/src/Api/ConsolidationV1/Requests/ModifyConsolidation.php +++ b/src/Api/ConsolidationV1/Requests/ModifyConsolidation.php @@ -56,7 +56,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ModifyConsolidationResponse::class, - 400 => ErrorResponseVo::class, + 400, 429 => ErrorResponseVo::class, 401 => ErrorResponseVo2::class, 403 => ErrorResponseVo3::class, 404 => ErrorResponseVo4::class, diff --git a/src/Api/ConsolidationV1/Requests/RetrieveConsolidation.php b/src/Api/ConsolidationV1/Requests/RetrieveConsolidation.php index 6c2d68ce..195575e1 100644 --- a/src/Api/ConsolidationV1/Requests/RetrieveConsolidation.php +++ b/src/Api/ConsolidationV1/Requests/RetrieveConsolidation.php @@ -55,7 +55,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => RetrieveConsolidationResponse::class, - 400 => ErrorResponseVo::class, + 400, 429 => ErrorResponseVo::class, 401 => ErrorResponseVo2::class, 403 => ErrorResponseVo3::class, 404 => ErrorResponseVo4::class, diff --git a/src/Api/FreightLTLV1/Requests/CancelFreightPickup.php b/src/Api/FreightLTLV1/Requests/CancelFreightPickup.php index dcf007df..4b14f562 100644 --- a/src/Api/FreightLTLV1/Requests/CancelFreightPickup.php +++ b/src/Api/FreightLTLV1/Requests/CancelFreightPickup.php @@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): PudcResponseVoCancelP $status = $response->status(); $responseCls = match ($status) { 200 => PudcResponseVoCancelPickup::class, - 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/FreightLTLV1/Requests/CheckFreightPickupAvailability.php b/src/Api/FreightLTLV1/Requests/CheckFreightPickupAvailability.php index 9e51f762..132610b2 100644 --- a/src/Api/FreightLTLV1/Requests/CheckFreightPickupAvailability.php +++ b/src/Api/FreightLTLV1/Requests/CheckFreightPickupAvailability.php @@ -53,7 +53,7 @@ public function createDtoFromResponse(Response $response): PudcResponseVoPickupA $status = $response->status(); $responseCls = match ($status) { 200 => PudcResponseVoPickupAvailaibility::class, - 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/FreightLTLV1/Requests/CreateFreightPickup.php b/src/Api/FreightLTLV1/Requests/CreateFreightPickup.php index a1ccf576..c85597b8 100644 --- a/src/Api/FreightLTLV1/Requests/CreateFreightPickup.php +++ b/src/Api/FreightLTLV1/Requests/CreateFreightPickup.php @@ -49,7 +49,7 @@ public function createDtoFromResponse(Response $response): PudcResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => PudcResponseVo::class, - 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/FreightLTLV1/Requests/FreightRateQuote.php b/src/Api/FreightLTLV1/Requests/FreightRateQuote.php index c5dd454b..10f2a6a0 100644 --- a/src/Api/FreightLTLV1/Requests/FreightRateQuote.php +++ b/src/Api/FreightLTLV1/Requests/FreightRateQuote.php @@ -52,7 +52,7 @@ public function createDtoFromResponse(Response $response): RatcResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => RatcResponseVo::class, - 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/FreightLTLV1/Requests/FreightShipment.php b/src/Api/FreightLTLV1/Requests/FreightShipment.php index ce993809..cd39a45d 100644 --- a/src/Api/FreightLTLV1/Requests/FreightShipment.php +++ b/src/Api/FreightLTLV1/Requests/FreightShipment.php @@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVo::class, - 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/GlobalTradeV1/Requests/ShipmentRegulatoryDetails.php b/src/Api/GlobalTradeV1/Requests/ShipmentRegulatoryDetails.php index baa828a9..46804d88 100644 --- a/src/Api/GlobalTradeV1/Requests/ShipmentRegulatoryDetails.php +++ b/src/Api/GlobalTradeV1/Requests/ShipmentRegulatoryDetails.php @@ -50,7 +50,7 @@ public function createDtoFromResponse(Response $response): GticResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => GticResponseVo::class, - 400, 401, 404, 422 => ErrorResponseVo::class, + 400, 401, 404, 422, 429 => ErrorResponseVo::class, 500 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/GroundEODCloseV1/Requests/GroundPerformEndOfDayClose.php b/src/Api/GroundEODCloseV1/Requests/GroundPerformEndOfDayClose.php index fb040ef9..f91abd5e 100644 --- a/src/Api/GroundEODCloseV1/Requests/GroundPerformEndOfDayClose.php +++ b/src/Api/GroundEODCloseV1/Requests/GroundPerformEndOfDayClose.php @@ -47,7 +47,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVo::class, - 400, 401, 403, 404, 503 => ErrorResponseVo::class, + 400, 401, 403, 404, 503, 429 => ErrorResponseVo::class, 500 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/GroundEODCloseV1/Requests/PerformEndOfDayClose.php b/src/Api/GroundEODCloseV1/Requests/PerformEndOfDayClose.php index b7a19a94..a318e115 100644 --- a/src/Api/GroundEODCloseV1/Requests/PerformEndOfDayClose.php +++ b/src/Api/GroundEODCloseV1/Requests/PerformEndOfDayClose.php @@ -47,7 +47,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVo::class, - 400, 401, 403, 404, 503 => ErrorResponseVo::class, + 400, 401, 403, 404, 503, 429 => ErrorResponseVo::class, 500 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/LocationsSearchV1/Requests/FindLocation.php b/src/Api/LocationsSearchV1/Requests/FindLocation.php index 36f760b6..dfd2f629 100644 --- a/src/Api/LocationsSearchV1/Requests/FindLocation.php +++ b/src/Api/LocationsSearchV1/Requests/FindLocation.php @@ -53,7 +53,7 @@ public function createDtoFromResponse(Response $response): LoccResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => LoccResponseVo::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/OpenShipV1/Requests/AddOpenShipmentPackages.php b/src/Api/OpenShipV1/Requests/AddOpenShipmentPackages.php index 63794534..1fbb185d 100644 --- a/src/Api/OpenShipV1/Requests/AddOpenShipmentPackages.php +++ b/src/Api/OpenShipV1/Requests/AddOpenShipmentPackages.php @@ -52,7 +52,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoAddPackagesToOpenShipment::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/OpenShipV1/Requests/ConfirmOpenShipment.php b/src/Api/OpenShipV1/Requests/ConfirmOpenShipment.php index bce2d6e4..5c41a116 100644 --- a/src/Api/OpenShipV1/Requests/ConfirmOpenShipment.php +++ b/src/Api/OpenShipV1/Requests/ConfirmOpenShipment.php @@ -49,7 +49,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoConfirmOpenShipment::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/OpenShipV1/Requests/CreateOpenShipmentt.php b/src/Api/OpenShipV1/Requests/CreateOpenShipmentt.php index f6d5a901..7e176c23 100644 --- a/src/Api/OpenShipV1/Requests/CreateOpenShipmentt.php +++ b/src/Api/OpenShipV1/Requests/CreateOpenShipmentt.php @@ -52,7 +52,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoCreateOpenShipment::class, - 400, 401, 403, 404, 500 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 429 => ErrorResponseVo::class, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/OpenShipV1/Requests/DeleteOpenShipment.php b/src/Api/OpenShipV1/Requests/DeleteOpenShipment.php index d21a9365..35dd4ea7 100644 --- a/src/Api/OpenShipV1/Requests/DeleteOpenShipment.php +++ b/src/Api/OpenShipV1/Requests/DeleteOpenShipment.php @@ -49,7 +49,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoDeleteOpenShipment::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/OpenShipV1/Requests/DeleteOpenShipmentPackages.php b/src/Api/OpenShipV1/Requests/DeleteOpenShipmentPackages.php index d7457d8f..2f9451ee 100644 --- a/src/Api/OpenShipV1/Requests/DeleteOpenShipmentPackages.php +++ b/src/Api/OpenShipV1/Requests/DeleteOpenShipmentPackages.php @@ -49,7 +49,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoDeletePackages::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/OpenShipV1/Requests/GetOpenShipmentResults.php b/src/Api/OpenShipV1/Requests/GetOpenShipmentResults.php index 58bf86b4..f5c36f24 100644 --- a/src/Api/OpenShipV1/Requests/GetOpenShipmentResults.php +++ b/src/Api/OpenShipV1/Requests/GetOpenShipmentResults.php @@ -49,7 +49,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoGetOpenShipmentResults::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/OpenShipV1/Requests/ModifyOpenShipment.php b/src/Api/OpenShipV1/Requests/ModifyOpenShipment.php index 5c712cb3..2e93c02d 100644 --- a/src/Api/OpenShipV1/Requests/ModifyOpenShipment.php +++ b/src/Api/OpenShipV1/Requests/ModifyOpenShipment.php @@ -49,7 +49,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoModifyOpenShipment::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/OpenShipV1/Requests/ModifyOpenShipmentPackages.php b/src/Api/OpenShipV1/Requests/ModifyOpenShipmentPackages.php index 04a691ba..e9a8e1bd 100644 --- a/src/Api/OpenShipV1/Requests/ModifyOpenShipmentPackages.php +++ b/src/Api/OpenShipV1/Requests/ModifyOpenShipmentPackages.php @@ -52,7 +52,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoModifyPackageInOpenShipment::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/OpenShipV1/Requests/RetrieveOpenShipment.php b/src/Api/OpenShipV1/Requests/RetrieveOpenShipment.php index 9417bbc6..4ae6e10e 100644 --- a/src/Api/OpenShipV1/Requests/RetrieveOpenShipment.php +++ b/src/Api/OpenShipV1/Requests/RetrieveOpenShipment.php @@ -49,7 +49,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoRetrieveOpenShipment::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/OpenShipV1/Requests/RetrieveOpenShipmentPackages.php b/src/Api/OpenShipV1/Requests/RetrieveOpenShipmentPackages.php index 2f5117fc..8ee0aaa5 100644 --- a/src/Api/OpenShipV1/Requests/RetrieveOpenShipmentPackages.php +++ b/src/Api/OpenShipV1/Requests/RetrieveOpenShipmentPackages.php @@ -49,7 +49,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoRetrievePackageInOpenShipment::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/PickupRequestV1/Requests/CancelPickup.php b/src/Api/PickupRequestV1/Requests/CancelPickup.php index 9cbe2284..a19f9771 100644 --- a/src/Api/PickupRequestV1/Requests/CancelPickup.php +++ b/src/Api/PickupRequestV1/Requests/CancelPickup.php @@ -53,7 +53,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => PudcResponseVoCancelPickup::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/PickupRequestV1/Requests/CheckPickupAvailability.php b/src/Api/PickupRequestV1/Requests/CheckPickupAvailability.php index 4e83cf1c..951ca97c 100644 --- a/src/Api/PickupRequestV1/Requests/CheckPickupAvailability.php +++ b/src/Api/PickupRequestV1/Requests/CheckPickupAvailability.php @@ -51,7 +51,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => PudcResponseVoPickupAvailability::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/PickupRequestV1/Requests/CreatePickup.php b/src/Api/PickupRequestV1/Requests/CreatePickup.php index f3c23055..e544f0a5 100644 --- a/src/Api/PickupRequestV1/Requests/CreatePickup.php +++ b/src/Api/PickupRequestV1/Requests/CreatePickup.php @@ -51,7 +51,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => PudcResponseVoCreatePickup::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/PostalCodeValidationV1/Requests/ValidatePostal.php b/src/Api/PostalCodeValidationV1/Requests/ValidatePostal.php index 0c1c518b..0aa7a4a9 100644 --- a/src/Api/PostalCodeValidationV1/Requests/ValidatePostal.php +++ b/src/Api/PostalCodeValidationV1/Requests/ValidatePostal.php @@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): CountryCxsResponseVo| $status = $response->status(); $responseCls = match ($status) { 200 => CountryCxsResponseVo::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/RatesAndTransitTimesV1/Requests/RateAndTransitTimes.php b/src/Api/RatesAndTransitTimesV1/Requests/RateAndTransitTimes.php index 5991eb79..dcb2a985 100644 --- a/src/Api/RatesAndTransitTimesV1/Requests/RateAndTransitTimes.php +++ b/src/Api/RatesAndTransitTimesV1/Requests/RateAndTransitTimes.php @@ -54,7 +54,7 @@ public function createDtoFromResponse(Response $response): RatcResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => RatcResponseVo::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ServiceAvailabilityV1/Requests/GetPackageAndServiceOptions.php b/src/Api/ServiceAvailabilityV1/Requests/GetPackageAndServiceOptions.php index 4e5d5b4f..0e71703b 100644 --- a/src/Api/ServiceAvailabilityV1/Requests/GetPackageAndServiceOptions.php +++ b/src/Api/ServiceAvailabilityV1/Requests/GetPackageAndServiceOptions.php @@ -52,7 +52,7 @@ public function createDtoFromResponse(Response $response): CmdcResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => CmdcResponseVo::class, - 400, 401, 403, 404, 503 => ErrorResponseVo2::class, + 400, 401, 403, 404, 503, 429 => ErrorResponseVo2::class, 500 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ServiceAvailabilityV1/Requests/GetSpecialServiceOptions.php b/src/Api/ServiceAvailabilityV1/Requests/GetSpecialServiceOptions.php index 304a3d84..c3426476 100644 --- a/src/Api/ServiceAvailabilityV1/Requests/GetSpecialServiceOptions.php +++ b/src/Api/ServiceAvailabilityV1/Requests/GetSpecialServiceOptions.php @@ -53,7 +53,7 @@ public function createDtoFromResponse(Response $response): AvailabilitycxsRespon $status = $response->status(); $responseCls = match ($status) { 200 => AvailabilitycxsResponseVo::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ServiceAvailabilityV1/Requests/RetrieveServicesAndTransitTimes.php b/src/Api/ServiceAvailabilityV1/Requests/RetrieveServicesAndTransitTimes.php index 7ab51b20..6bd1a6cd 100644 --- a/src/Api/ServiceAvailabilityV1/Requests/RetrieveServicesAndTransitTimes.php +++ b/src/Api/ServiceAvailabilityV1/Requests/RetrieveServicesAndTransitTimes.php @@ -50,7 +50,7 @@ public function createDtoFromResponse(Response $response): TransitTimeOutputVo|E $status = $response->status(); $responseCls = match ($status) { 200 => TransitTimeOutputVo::class, - 400, 401, 404 => ErrorResponseVo::class, + 400, 401, 404, 429 => ErrorResponseVo::class, 403, 500 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ShipDGHazmatV1/Requests/CancelShipment.php b/src/Api/ShipDGHazmatV1/Requests/CancelShipment.php index a7572f6a..5d2a9053 100644 --- a/src/Api/ShipDGHazmatV1/Requests/CancelShipment.php +++ b/src/Api/ShipDGHazmatV1/Requests/CancelShipment.php @@ -56,7 +56,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoCancelShipment::class, - 400 => ErrorResponseVo2::class, + 400, 429 => ErrorResponseVo2::class, 401 => ErrorResponseVo3::class, 403 => ErrorResponseVo4::class, 404 => ErrorResponseVo5::class, diff --git a/src/Api/ShipDGHazmatV1/Requests/DgHazShipment.php b/src/Api/ShipDGHazmatV1/Requests/DgHazShipment.php index fa9d654d..dc03ecd6 100644 --- a/src/Api/ShipDGHazmatV1/Requests/DgHazShipment.php +++ b/src/Api/ShipDGHazmatV1/Requests/DgHazShipment.php @@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVo::class, - 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ShipDGHazmatV1/Requests/ShipmentPackageValidate.php b/src/Api/ShipDGHazmatV1/Requests/ShipmentPackageValidate.php index 1f45f1ef..d8bc6aae 100644 --- a/src/Api/ShipDGHazmatV1/Requests/ShipmentPackageValidate.php +++ b/src/Api/ShipDGHazmatV1/Requests/ShipmentPackageValidate.php @@ -57,7 +57,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVoValidat $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoValidate::class, - 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ShipV1/Requests/CancelShipment.php b/src/Api/ShipV1/Requests/CancelShipment.php index f8df0d82..658a6466 100644 --- a/src/Api/ShipV1/Requests/CancelShipment.php +++ b/src/Api/ShipV1/Requests/CancelShipment.php @@ -52,7 +52,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoCancelShipment::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ShipV1/Requests/CancelTag.php b/src/Api/ShipV1/Requests/CancelTag.php index f53ab19b..374f20f7 100644 --- a/src/Api/ShipV1/Requests/CancelTag.php +++ b/src/Api/ShipV1/Requests/CancelTag.php @@ -52,7 +52,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVo::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ShipV1/Requests/CreateShipment.php b/src/Api/ShipV1/Requests/CreateShipment.php index 3311de49..8d95e24b 100644 --- a/src/Api/ShipV1/Requests/CreateShipment.php +++ b/src/Api/ShipV1/Requests/CreateShipment.php @@ -53,7 +53,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoShipShipment::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ShipV1/Requests/CreateTag.php b/src/Api/ShipV1/Requests/CreateTag.php index fd33d650..c67bc03f 100644 --- a/src/Api/ShipV1/Requests/CreateTag.php +++ b/src/Api/ShipV1/Requests/CreateTag.php @@ -49,7 +49,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVoCreateT $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoCreateTag::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ShipV1/Requests/GetConfirmedShipmentAsyncResults.php b/src/Api/ShipV1/Requests/GetConfirmedShipmentAsyncResults.php index b1b8ee17..006bb415 100644 --- a/src/Api/ShipV1/Requests/GetConfirmedShipmentAsyncResults.php +++ b/src/Api/ShipV1/Requests/GetConfirmedShipmentAsyncResults.php @@ -49,7 +49,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoGetOpenShipmentResults::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/ShipV1/Requests/ShipmentPackageValidate.php b/src/Api/ShipV1/Requests/ShipmentPackageValidate.php index 6838e7b3..815c8259 100644 --- a/src/Api/ShipV1/Requests/ShipmentPackageValidate.php +++ b/src/Api/ShipV1/Requests/ShipmentPackageValidate.php @@ -57,7 +57,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVoValidat $status = $response->status(); $responseCls = match ($status) { 200 => ShpcResponseVoValidate::class, - 400, 401, 403, 404, 500 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/TrackV1/Requests/SendNotification.php b/src/Api/TrackV1/Requests/SendNotification.php index b66d9893..fdc6f2ab 100644 --- a/src/Api/TrackV1/Requests/SendNotification.php +++ b/src/Api/TrackV1/Requests/SendNotification.php @@ -51,7 +51,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => TrkcResponseVoNotifications::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/TrackV1/Requests/TrackByReferences.php b/src/Api/TrackV1/Requests/TrackByReferences.php index f5b76043..4aea9b78 100644 --- a/src/Api/TrackV1/Requests/TrackByReferences.php +++ b/src/Api/TrackV1/Requests/TrackByReferences.php @@ -52,7 +52,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => TrkcResponseVoReferenceNumber::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/TrackV1/Requests/TrackByTcn.php b/src/Api/TrackV1/Requests/TrackByTcn.php index 9084c472..5b519d80 100644 --- a/src/Api/TrackV1/Requests/TrackByTcn.php +++ b/src/Api/TrackV1/Requests/TrackByTcn.php @@ -50,7 +50,7 @@ public function createDtoFromResponse(Response $response): TrkcResponseVoTcn|Err $status = $response->status(); $responseCls = match ($status) { 200 => TrkcResponseVoTcn::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/TrackV1/Requests/TrackByTrackingNumber.php b/src/Api/TrackV1/Requests/TrackByTrackingNumber.php index c0069b3b..246e5b68 100644 --- a/src/Api/TrackV1/Requests/TrackByTrackingNumber.php +++ b/src/Api/TrackV1/Requests/TrackByTrackingNumber.php @@ -52,7 +52,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 200 => TrkcResponseVoTrackingNumber::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/TrackV1/Requests/TrackMultiplePieceShipment.php b/src/Api/TrackV1/Requests/TrackMultiplePieceShipment.php index cfd6bde4..ddd7066a 100644 --- a/src/Api/TrackV1/Requests/TrackMultiplePieceShipment.php +++ b/src/Api/TrackV1/Requests/TrackMultiplePieceShipment.php @@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): TrkcResponseVoAssocia $status = $response->status(); $responseCls = match ($status) { 200 => TrkcResponseVoAssociated::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/TrackV1/Requests/TrackingDocuments.php b/src/Api/TrackV1/Requests/TrackingDocuments.php index 3d6beed6..f3b31b3e 100644 --- a/src/Api/TrackV1/Requests/TrackingDocuments.php +++ b/src/Api/TrackV1/Requests/TrackingDocuments.php @@ -47,7 +47,7 @@ public function createDtoFromResponse(Response $response): TrkcResponseVoSpod|Er $status = $response->status(); $responseCls = match ($status) { 200 => TrkcResponseVoSpod::class, - 400, 500 => ErrorResponseVo::class, + 400, 500, 429 => ErrorResponseVo::class, 401, 403, 404, 503 => ErrorResponseVo2::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/TradeDocumentsUploadV1/Requests/ImageUploadServiceInfo.php b/src/Api/TradeDocumentsUploadV1/Requests/ImageUploadServiceInfo.php index 3623fb6b..7e5ed47b 100644 --- a/src/Api/TradeDocumentsUploadV1/Requests/ImageUploadServiceInfo.php +++ b/src/Api/TradeDocumentsUploadV1/Requests/ImageUploadServiceInfo.php @@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): ImageUploadServiceOut $status = $response->status(); $responseCls = match ($status) { 201 => ImageUploadServiceOutputVo::class, - 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/TradeDocumentsUploadV1/Requests/UploadEtdFiles.php b/src/Api/TradeDocumentsUploadV1/Requests/UploadEtdFiles.php index 87e92302..ecd10bb8 100644 --- a/src/Api/TradeDocumentsUploadV1/Requests/UploadEtdFiles.php +++ b/src/Api/TradeDocumentsUploadV1/Requests/UploadEtdFiles.php @@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): DocumentResponseOutpu $status = $response->status(); $responseCls = match ($status) { 201 => DocumentResponseOutputVo::class, - 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class, + 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") }; diff --git a/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEncodedEtDfiles.php b/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEncodedEtDfiles.php index e155b53c..550a0151 100644 --- a/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEncodedEtDfiles.php +++ b/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEncodedEtDfiles.php @@ -52,7 +52,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 201 => MultiBasePreResponse2::class, - 400 => ErrorResponseVo2::class, + 400, 429 => ErrorResponseVo2::class, 401, 403, 500, 503 => ErrorResponseVo3::class, 404 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") diff --git a/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEtDfiles.php b/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEtDfiles.php index fc217e34..22c0fc7c 100644 --- a/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEtDfiles.php +++ b/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEtDfiles.php @@ -50,7 +50,7 @@ public function createDtoFromResponse( $status = $response->status(); $responseCls = match ($status) { 201 => MultiBasePreResponse::class, - 400 => ErrorResponseVo2::class, + 400, 429 => ErrorResponseVo2::class, 401, 403, 500, 503 => ErrorResponseVo3::class, 404 => ErrorResponseVo::class, default => throw new Exception("Unhandled response status: {$status}") diff --git a/src/Auth/NullAuthenticator.php b/src/Auth/NullAuthenticator.php deleted file mode 100644 index dfb94c87..00000000 --- a/src/Auth/NullAuthenticator.php +++ /dev/null @@ -1,18 +0,0 @@ -value, PHP_URL_HOST) ?: ''; } + + public function getProductionEndpoint(): self + { + return $this->_getEndpointPair($this, false); + } + + public function getSandboxEndpoint(): self + { + return $this->_getEndpointPair($this, true); + } + + public function isProduction(): bool + { + return $this->_getEndpointPair($this, false) === $this; + } + + public function isSandbox(): bool + { + return $this->_getEndpointPair($this, true) === $this; + } + + private function _getEndpointPair(Endpoint $endpoint, bool $sandbox): Endpoint + { + return match ($endpoint) { + Endpoint::PROD, Endpoint::SANDBOX => $sandbox ? Endpoint::SANDBOX : Endpoint::PROD, + Endpoint::PROD_DOCUMENTS_UPLOAD, Endpoint::SANDBOX_DOCUMENTS_UPLOAD => $sandbox ? Endpoint::SANDBOX_DOCUMENTS_UPLOAD : Endpoint::PROD_DOCUMENTS_UPLOAD, + default => throw new \Exception('Unknown FedEx endpoint.') + }; + } } diff --git a/src/FedEx.php b/src/FedEx.php index b9fb235f..c4568004 100644 --- a/src/FedEx.php +++ b/src/FedEx.php @@ -7,9 +7,12 @@ use Closure; use InvalidArgumentException; use RuntimeException; +use Saloon\Contracts\Authenticator; use Saloon\Http\Connector; use Saloon\Http\PendingRequest; use Saloon\Http\Request; +use Saloon\RateLimitPlugin\Contracts\RateLimitStore; +use Saloon\RateLimitPlugin\Stores\MemoryStore; use Saloon\Traits\OAuth2\ClientCredentialsGrant; use Saloon\Traits\Plugins\AlwaysThrowOnErrors; use ShipStream\FedEx\Api\AccountRegistrationV1; @@ -25,13 +28,13 @@ use ShipStream\FedEx\Api\PickupRequestV1; use ShipStream\FedEx\Api\PostalCodeValidationV1; use ShipStream\FedEx\Api\RatesAndTransitTimesV1; -use ShipStream\FedEx\Api\ShipV1; use ShipStream\FedEx\Api\ShipDGHazmatV1; +use ShipStream\FedEx\Api\ShipV1; use ShipStream\FedEx\Api\TrackV1; use ShipStream\FedEx\Api\TradeDocumentsUploadV1; use ShipStream\FedEx\Auth\MemoryCache; -use ShipStream\FedEx\Auth\NullAuthenticator; use ShipStream\FedEx\Contracts\TokenCache; +use ShipStream\FedEx\Contracts\TokenLock; use ShipStream\FedEx\Enums\Endpoint; use ShipStream\FedEx\Enums\GrantType; @@ -51,6 +54,8 @@ public function __construct( public ?string $childSecret = null, public ?bool $proprietaryChild = false, public ?TokenCache $tokenCache = new MemoryCache(), + public ?TokenLock $tokenLock = null, + public ?RateLimitStore $rateLimitStore = new MemoryStore(), public ?Closure $transactionIdGenerator = null, public ?string $locale = null, ) { @@ -60,15 +65,6 @@ public function __construct( $this->oauthConfig()->setClientId($clientId); $this->oauthConfig()->setClientSecret($clientSecret); - - $cacheKey = $this->tokenCacheKey(); - $authenticator = $tokenCache::get($cacheKey); - if (! $authenticator) { - $authenticator = $this->getAccessToken(); - $tokenCache::set($cacheKey, $authenticator); - } - - $this->authenticate($authenticator); } public function resolveBaseUrl(): string @@ -76,6 +72,27 @@ public function resolveBaseUrl(): string return $this->endpoint->value; } + protected function defaultAuth(): Authenticator + { + $cacheKey = $this->tokenCacheKey(); + $authenticator = $this->tokenCache::get($cacheKey); + if (! $authenticator) { + try { + $this->tokenLock?->lock($cacheKey); + // Re-check cache after acquiring lock in case another process already fetched the token + $authenticator = $this->tokenCache::get($cacheKey); + if (! $authenticator) { + $authenticator = $this->getAccessToken(); + $this->tokenCache::set($cacheKey, $authenticator); + } + } finally { + $this->tokenLock?->unlock($cacheKey); + } + } + + return $authenticator; + } + public function boot(PendingRequest $pendingRequest): void { if ($this->transactionIdGenerator) { @@ -106,8 +123,6 @@ public function addressValidationV1(): AddressValidationV1\Api public function authorizationV1(): AuthorizationV1\Api { - $this->authenticate(new NullAuthenticator()); - return new AuthorizationV1\Api($this); } @@ -175,7 +190,13 @@ public function tokenCacheKey(): string { $childKeyStr = $this->childKey ? '.'.$this->childKey : ''; - return $this->clientId.$childKeyStr; + $key = $this->clientId.$childKeyStr; + + if ($this->endpoint->isSandbox()) { + $key .= '.sandbox'; + } + + return $key; } protected function resolveAccessTokenRequest(): Request @@ -196,6 +217,6 @@ protected function resolveAccessTokenRequest(): Request $args['grantType'] = $args['grantType']->value; - return new ApiAuthorization(new FullSchema(...$args)); + return new ApiAuthorization(new FullSchema(...$args), $this->rateLimitStore); } } diff --git a/src/Generator/Generators/RequestGenerator.php b/src/Generator/Generators/RequestGenerator.php index 1ea947a4..056025e7 100644 --- a/src/Generator/Generators/RequestGenerator.php +++ b/src/Generator/Generators/RequestGenerator.php @@ -10,6 +10,14 @@ use Illuminate\Support\Collection; use Illuminate\Support\Str; use Nette\PhpGenerator\PhpFile; +use Saloon\Contracts\Authenticator; +use Saloon\Helpers\URLHelper; +use Saloon\Http\Auth\NullAuthenticator; +use Saloon\Http\PendingRequest; +use Saloon\RateLimitPlugin\Contracts\RateLimitStore; +use Saloon\RateLimitPlugin\Limit; +use Saloon\RateLimitPlugin\Stores\MemoryStore; +use Saloon\RateLimitPlugin\Traits\HasRateLimits; class RequestGenerator extends SDKRequestGenerator { @@ -40,6 +48,60 @@ protected function generateRequestClass(Endpoint $endpoint): PhpFile ); } + if ($endpoint->name === 'API Authorization') { + // Add rate limit to authorization API to avoid triggering a 10min lockout + $namespaces = $classFile->getNamespaces(); + $namespace = reset($namespaces); + $namespace->addUse(HasRateLimits::class); + $namespace->addUse(Limit::class); + $namespace->addUse(RateLimitStore::class); + $namespace->addUse(MemoryStore::class); + $classes = $classFile->getClasses(); + $class = reset($classes); + $class->addTrait(HasRateLimits::class); + $class->addMethod('resolveLimits') + ->setPublic() + ->setReturnType('array') + ->addBody('return [') + ->addBody(" Limit::allow(requests: 14)->everySeconds(5)->name('burst-threshold'),") + ->addBody(" Limit::allow(requests: 119)->everySeconds(120)->name('average-threshold'),") + ->addBody('];'); + $class->addMethod('resolveRateLimitStore') + ->setPublic() + ->setReturnType(RateLimitStore::class) + ->addBody('return new MemoryStore();'); + + $constructor = $class->getMethod('__construct'); + $constructor->addParameter('rateLimitStore') + ->setDefaultValue(null) + ->setNullable(true) + ->setType(RateLimitStore::class); + $constructor->addBody('$this->rateLimitStore = $rateLimitStore;'); + + // Disable authorization for authorization API + $namespace->addUse(Authenticator::class); + $namespace->addUse(NullAuthenticator::class); + $class->addMethod('defaultAuth') + ->setPublic() + ->setReturnType(Authenticator::class) + ->addBody('return new NullAuthenticator();'); + + // Ensure correct base url is used for authorization + $namespace->addUse(URLHelper::class); + $namespace->addUse(PendingRequest::class); + $namespace->addUse(\ShipStream\FedEx\Enums\Endpoint::class); + $class->addMethod('boot') + ->setPublic() + ->setReturnType('void') + ->addBody('// Ensure that authorization always uses the correct base url so that connectors with a different base url can still authenticate') + ->addBody('/** @var Endpoint $endpoint */') + ->addBody('if ($endpoint = ($pendingRequest->getConnector()->endpoint ?? null)) {') + ->addBody(' $pendingRequest->setUrl(URLHelper::join($endpoint->isProduction() ? Endpoint::PROD->value : Endpoint::SANDBOX->value, $pendingRequest->getRequest()->resolveEndpoint()));') + ->addBody('}') + ->addParameter('pendingRequest') + ->setType(PendingRequest::class); + } + return $classFile; } } diff --git a/src/Generator/Generators/ResourceGenerator.php b/src/Generator/Generators/ResourceGenerator.php index 0561f373..f3cc410a 100644 --- a/src/Generator/Generators/ResourceGenerator.php +++ b/src/Generator/Generators/ResourceGenerator.php @@ -80,7 +80,15 @@ public function generateResourceClass(string $resourceName, array $endpoints): ? } foreach ($endpoint->queryParameters as $parameter) { - if (in_array($parameter->name, $this->config->ignoredQueryParams)) { + if (in_array(strtolower($parameter->rawName), $this->config->ignoredParams['query'])) { + continue; + } + MethodGeneratorHelper::addParameterToMethod($method, $parameter); + $args[] = new Literal(sprintf('$%s', NameHelper::safeVariableName($parameter->name))); + } + + foreach ($endpoint->headerParameters as $parameter) { + if (in_array(strtolower($parameter->rawName), $this->config->ignoredParams['header'])) { continue; } MethodGeneratorHelper::addParameterToMethod($method, $parameter); diff --git a/src/Generator/Schema/Refactorer.php b/src/Generator/Schema/Refactorer.php index 8b55a7fe..eff1c59e 100644 --- a/src/Generator/Schema/Refactorer.php +++ b/src/Generator/Schema/Refactorer.php @@ -26,8 +26,7 @@ class Refactorer public function __construct( private SchemaVersion $schemaVersion, private array $schemas - ) { - } + ) {} public function combine(): void { @@ -107,6 +106,16 @@ public function clean(): stdClass $operation->responses->{'404'}->content->{'application/json'} = $mediaType; } + // All FedEx endpoints are missing responses schemas for 429 responses + if (! isset($operation->responses->{'429'})) { + $operation->responses->{'429'} = new stdClass; + $operation->responses->{'429'}->description = 'Too many requests.'; + $operation->responses->{'429'}->content = new stdClass; + $operation->responses->{'429'}->content->{'application/json'} = new stdClass; + $operation->responses->{'429'}->content->{'application/json'}->schema = new stdClass; + $operation->responses->{'429'}->content->{'application/json'}->schema->{'$ref'} = $operation->responses->{'400'}->content->{'application/json'}->schema->{'$ref'} ?? '#/components/schemas/ErrorResponseVO'; + } + if (isset($operation->requestBody)) { foreach ($operation->requestBody->content as $contentType => $mediaType) { if (isset($mediaType->schema->oneOf)) { diff --git a/tests/FedExConnectorTest.php b/tests/FedExConnectorTest.php index cc4853bd..18a40a36 100644 --- a/tests/FedExConnectorTest.php +++ b/tests/FedExConnectorTest.php @@ -48,11 +48,13 @@ public function testThrowsUnauthorizedExceptionIfCredentialsAreInvalid(): void { $this->expectException(UnauthorizedException::class); - new FedEx( + $connector = new FedEx( clientId: 'invalidId', clientSecret: 'invalidSecret', endpoint: Endpoint::SANDBOX, ); + + $connector->getAccessToken(); } public function testFetchesNewAccessToken(): void @@ -103,15 +105,21 @@ public function testFetchesNewAccessTokenIfCacheExpired(): void $expiredAuthenticator = new AccessTokenAuthenticator($accessToken, expiresAt: new DateTimeImmutable('-10 seconds')); MemoryCache::set($connector->tokenCacheKey(), $expiredAuthenticator); + $newAccessToken = $connector->getAuthenticator()->accessToken; + + $this->assertNotEquals($accessToken, $newAccessToken); + } + + public function testEndpointIsOverriddenForAuthorization(): void + { $connector = new FedEx( clientId: $this->clientId, clientSecret: $this->clientSecret, - endpoint: Endpoint::SANDBOX, + endpoint: Endpoint::SANDBOX_DOCUMENTS_UPLOAD, // Incorrect for authorization ); - $newAccessToken = $connector->getAuthenticator()->accessToken; - - $this->assertNotEquals($accessToken, $newAccessToken); + $authenticator = $connector->getAccessToken(); + $this->assertInstanceOf(AccessTokenAuthenticator::class, $authenticator); } public function testGeneratesTransactionIds(): void