From 2c64bb14621707b467b22eb8041c0241ba508572 Mon Sep 17 00:00:00 2001 From: Casper Bakker Date: Tue, 2 Jan 2024 08:48:58 +0100 Subject: [PATCH] Script to download latest specs --- README.md | 9 +- composer.json | 1 + src/Client.php | 11 +-- src/Model/RetailerInformationResponse.php | 18 ++++ src/Model/SubscriptionRequest.php | 6 ++ src/Model/SubscriptionResponse.php | 6 ++ src/OpenApi/ClientGenerator.php | 29 +++---- src/OpenApi/ModelGenerator.php | 26 +++--- src/OpenApi/SpecsDownloader.php | 29 +++++++ src/OpenApi/retailer.json | 101 ++++++++++++++-------- src/OpenApi/shared.json | 4 +- 11 files changed, 163 insertions(+), 77 deletions(-) create mode 100644 src/OpenApi/SpecsDownloader.php diff --git a/README.md b/README.md index 2eaf003..23a05e9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Bol.com Retailer API client for PHP -This is an open source PHP client for the [Bol.com Retailer API](https://api.bol.com/retailer/public/Retailer-API/v10/releasenotes.html) version 10.4. +This is an open source PHP client for the [Bol.com Retailer API](https://api.bol.com/retailer/public/Retailer-API/v10/releasenotes.html) version 10.6. ## Installation This project can easily be installed through Composer: @@ -179,10 +179,15 @@ Please follow the guidelines below if you want to contribute. - Keep in mind that we want to support PHP 7.1 as long as possible. ## Generated Models and Client -The Client and all models are generated by the supplied [Retailer API specifications](https://api.bol.com/retailer/public/apispec/Retailer%20API%20-%20v10) (`src/OpenApi/retailer.json`) and [Shared API specification](https://api.bol.com/retailer/public/apispec/Shared%20API%20-%20v10) (`src/OpenApi/shared.json`). These specifications are merged. Generating the code ensures there are no typos, not every operation needs a test and future (minor) updates to the specifications can easily be applied. To build the classes for the latest Bol Retailer API version, replace the two specification files with the latest version first. +The Client and all models are generated by the supplied [Retailer API specifications](https://api.bol.com/retailer/public/apispec/Retailer%20API%20-%20v10) (`src/OpenApi/retailer.json`) and [Shared API specification](https://api.bol.com/retailer/public/apispec/Shared%20API%20-%20v10) (`src/OpenApi/shared.json`). These specifications are merged. Generating the code ensures there are no typos, not every operation needs a test and future (minor) updates to the specifications can easily be applied. The generated classes contain all data required to properly map method arguments and response data to the models: the specifications are only used to generate them. +To build the classes for the latest Bol Retailer API version, let the code download the newest specs with this script: +``` +composer run-script download-specs +``` + ### Client The Client contains all operations specified in the specifications. The 'operationId' value is converted to camelCase and used as method name for each operation. diff --git a/composer.json b/composer.json index f813bc4..4fa81e0 100644 --- a/composer.json +++ b/composer.json @@ -41,6 +41,7 @@ "test": "phpunit", "check-style": "phpcs src tests", "fix-style": "phpcbf src tests", + "download-specs": "Picqer\\BolRetailerV10\\OpenApi\\SpecsDownloader::run", "generate-client": "Picqer\\BolRetailerV10\\OpenApi\\ClientGenerator::run", "generate-models": "Picqer\\BolRetailerV10\\OpenApi\\ModelGenerator::run" }, diff --git a/src/Client.php b/src/Client.php index 4676b3e..08e5fb1 100644 --- a/src/Client.php +++ b/src/Client.php @@ -1577,7 +1577,6 @@ public function getInvoiceRequests(?string $shipmentId = null, ?int $page = 1, ? /** * Uploads an invoice associated with shipment id. * @param string $shipmentId The id of the shipment associated with the invoice. - * @param string $invoice * @return Model\ProcessStatus|null * @throws Exception\ConnectException when an error occurred in the HTTP connection. * @throws Exception\ResponseException when an unexpected response was received. @@ -1585,18 +1584,12 @@ public function getInvoiceRequests(?string $shipmentId = null, ?int $page = 1, ? * @throws Exception\RateLimitException when the throttling limit has been reached for the API user. * @throws Exception\Exception when something unexpected went wrong. */ - public function uploadInvoice(string $shipmentId, string $invoice): ?Model\ProcessStatus + public function uploadInvoice(string $shipmentId): ?Model\ProcessStatus { $url = "retailer/shipments/invoices/{$shipmentId}"; $options = [ - 'multipart' => [ - [ - 'name' => 'invoice', - 'contents' => \GuzzleHttp\Psr7\Utils::tryFopen($invoice, 'r'), - ], - ], 'produces' => 'application/vnd.retailer.v10+json', - 'consumes' => 'multipart/form-data', + 'consumes' => 'application/json', ]; $responseTypes = [ '202' => Model\ProcessStatus::class, diff --git a/src/Model/RetailerInformationResponse.php b/src/Model/RetailerInformationResponse.php index 59db612..a60c7fb 100644 --- a/src/Model/RetailerInformationResponse.php +++ b/src/Model/RetailerInformationResponse.php @@ -19,6 +19,9 @@ public function getModelDefinition(): array return [ 'retailerId' => [ 'model' => null, 'enum' => null, 'array' => false ], 'displayName' => [ 'model' => null, 'enum' => null, 'array' => false ], + 'companyName' => [ 'model' => null, 'enum' => null, 'array' => false ], + 'vatNumber' => [ 'model' => null, 'enum' => null, 'array' => false ], + 'kvkNumber' => [ 'model' => null, 'enum' => null, 'array' => false ], 'registrationDate' => [ 'model' => null, 'enum' => null, 'array' => false ], 'topRetailer' => [ 'model' => null, 'enum' => null, 'array' => false ], 'ratingMethod' => [ 'model' => null, 'enum' => Enum\RetailerInformationResponseRatingMethod::class, 'array' => false ], @@ -37,6 +40,21 @@ public function getModelDefinition(): array */ public $displayName; + /** + * @var string The company name of the retailer. + */ + public $companyName; + + /** + * @var string The VAT number of the retailer. + */ + public $vatNumber; + + /** + * @var string The KVK number of the retailer. + */ + public $kvkNumber; + /** * @var string A date representing the registration date for the retailer within bol.com */ diff --git a/src/Model/SubscriptionRequest.php b/src/Model/SubscriptionRequest.php index f0c00e9..dcb604d 100644 --- a/src/Model/SubscriptionRequest.php +++ b/src/Model/SubscriptionRequest.php @@ -20,6 +20,7 @@ public function getModelDefinition(): array 'resources' => [ 'model' => null, 'enum' => null, 'array' => true ], 'url' => [ 'model' => null, 'enum' => null, 'array' => false ], 'subscriptionType' => [ 'model' => null, 'enum' => Enum\SubscriptionRequestSubscriptionType::class, 'array' => false ], + 'enabled' => [ 'model' => null, 'enum' => null, 'array' => false ], ]; } @@ -40,4 +41,9 @@ public function getModelDefinition(): array * events will be subscribed to. Be aware that certain event types are only available for specific types. */ public $subscriptionType; + + /** + * @var bool Whether the subscription is enabled and will receive notifications or not. Defaults to true. + */ + public $enabled; } diff --git a/src/Model/SubscriptionResponse.php b/src/Model/SubscriptionResponse.php index eae223f..6ec92fa 100644 --- a/src/Model/SubscriptionResponse.php +++ b/src/Model/SubscriptionResponse.php @@ -21,6 +21,7 @@ public function getModelDefinition(): array 'resources' => [ 'model' => null, 'enum' => null, 'array' => true ], 'url' => [ 'model' => null, 'enum' => null, 'array' => false ], 'subscriptionType' => [ 'model' => null, 'enum' => Enum\SubscriptionResponseSubscriptionType::class, 'array' => false ], + 'enabled' => [ 'model' => null, 'enum' => null, 'array' => false ], ]; } @@ -46,4 +47,9 @@ public function getModelDefinition(): array * events will be subscribed to. Be aware that certain event types are only available for specific types. */ public $subscriptionType; + + /** + * @var bool Whether the subscription is enabled and will receive notifications or not. Defaults to true. + */ + public $enabled; } diff --git a/src/OpenApi/ClientGenerator.php b/src/OpenApi/ClientGenerator.php index 15b56a4..9ad827a 100644 --- a/src/OpenApi/ClientGenerator.php +++ b/src/OpenApi/ClientGenerator.php @@ -1,6 +1,5 @@ kebabCaseToCamelCase($methodDefinition['operationId'] .'-'. $parameter['name'])); - $argument['php'] = 'Enum\\'.$wrappingType; + $wrappingType = ucfirst($this->kebabCaseToCamelCase($methodDefinition['operationId'] . '-' . $parameter['name'])); + $argument['php'] = 'Enum\\' . $wrappingType; $argument['doc'] = $argument['php']; $argument['name'] = $this->kebabCaseToCamelCase($parameter['name']); $argument['paramName'] = $parameter['name']; @@ -322,7 +321,7 @@ protected function extractArguments(array $methodDefinition): array if (isset($propSchema['type']) && $propSchema['type'] == 'array') { $itemsType = $this->getType($propSchema['items']['$ref']); - $argument['doc'] = 'Model\\'.$itemsType.'[]'; + $argument['doc'] = 'Model\\' . $itemsType . '[]'; $argument['php'] = 'array'; } elseif (isset($propSchema['type'])) { $wrappingType = static::$paramTypeMapping[$propSchema['type']]; @@ -330,16 +329,16 @@ protected function extractArguments(array $methodDefinition): array $argument['php'] = $wrappingType; } else { $wrappingType = $this->getType($propSchema['$ref']); - $argument['doc'] = 'Model\\'.$wrappingType; - $argument['php'] = 'Model\\'.$wrappingType; + $argument['doc'] = 'Model\\' . $wrappingType; + $argument['php'] = 'Model\\' . $wrappingType; } $argument['property'] = $property; $argument['name'] = $property; - $argument['wrapperPhp'] = 'Model\\'.$type; + $argument['wrapperPhp'] = 'Model\\' . $type; } - if (!isset($argument['property'])) { - $argument['php'] = 'Model\\'.$type; + if (! isset($argument['property'])) { + $argument['php'] = 'Model\\' . $type; $argument['doc'] = $argument['php']; $argument['name'] = lcfirst($type); } @@ -397,7 +396,7 @@ protected function argumentValueToString($argument): string protected function addQueryParams(array $arguments, array &$code): void { $amount = array_reduce($arguments, function ($amount, $argument) { - return $argument['in'] == 'query' ? $amount+1 : $amount; + return $argument['in'] == 'query' ? $amount + 1 : $amount; }); if ($amount == 0) { @@ -441,7 +440,7 @@ protected function addBodyParam(array $arguments, array &$code): void protected function addFormData(array $arguments, array &$code): void { $containsFileArgument = in_array(true, array_map( - static fn (array $argument): bool => $argument['is_file'] ?? false, + static fn(array $argument): bool => $argument['is_file'] ?? false, $arguments, )); $formData = []; @@ -524,8 +523,8 @@ protected function getReturnType(array $responses): array if (isset($refSchema['properties'][$property]['type'], $refSchema['properties'][$property]['items']['$ref']) && $refSchema['properties'][$property]['type'] == 'array') { return [ 'doc' => 'Model\\' . $this->getType( - $refSchema['properties'][$property]['items']['$ref'] - ) . '[]', + $refSchema['properties'][$property]['items']['$ref'] + ) . '[]', 'php' => 'array', 'property' => $property ]; @@ -555,7 +554,7 @@ protected function getEnumName(string $name): string // We add the first `_` for enums starting with a integer character $prefix = is_numeric($name[0]) ? '_' : ''; - return $prefix.strtoupper($name); + return $prefix . strtoupper($name); } protected function wrapComment(string $comment, string $linePrefix, int $maxLength = 120): string diff --git a/src/OpenApi/ModelGenerator.php b/src/OpenApi/ModelGenerator.php index 79f8626..fed7668 100644 --- a/src/OpenApi/ModelGenerator.php +++ b/src/OpenApi/ModelGenerator.php @@ -1,6 +1,5 @@ specs['paths'] as $path => $methodsDef) { foreach ($methodsDef as $method => $methodDef) { foreach ($methodDef['parameters'] ?? [] as $parameterDef) { - if (!isset($parameterDef['schema']['enum']) || !array_values(array_filter($parameterDef['schema']['enum']))) { + if (! isset($parameterDef['schema']['enum']) || ! array_values(array_filter($parameterDef['schema']['enum']))) { continue; } $this->generateEnum( - ucfirst($this->kebabCaseToCamelCase($methodDef['operationId'] .'-'. $parameterDef['name'])), + ucfirst($this->kebabCaseToCamelCase($methodDef['operationId'] . '-' . $parameterDef['name'])), static::$propTypeMapping[$parameterDef['schema']['type']], $parameterDef['schema']['enum'] ); @@ -65,12 +64,12 @@ public function generateEnums(): void foreach ($this->specs['components']['schemas'] as $type => $modelSchema) { foreach ($modelSchema['properties'] as $property => $propertyDef) { - if (!isset($propertyDef['enum']) || !array_values(array_filter($propertyDef['enum']))) { + if (! isset($propertyDef['enum']) || ! array_values(array_filter($propertyDef['enum']))) { continue; } $this->generateEnum( - ucfirst($this->kebabCaseToCamelCase($type .'-'. $property)), + ucfirst($this->kebabCaseToCamelCase($type . '-' . $property)), static::$propTypeMapping[$propertyDef['type']], $propertyDef['enum'] ); @@ -146,7 +145,7 @@ protected function generateSchema(string $type, array $modelSchema, array &$code $enum = 'null'; $array = 'false'; - if (isset($propDefinition['type']) && !isset($propDefinition['enum'])) { + if (isset($propDefinition['type']) && ! isset($propDefinition['enum'])) { if ($propDefinition['type'] == 'array') { $array = 'true'; if (isset($propDefinition['items']['$ref'])) { @@ -155,8 +154,8 @@ protected function generateSchema(string $type, array $modelSchema, array &$code } } elseif (isset($propDefinition['$ref'])) { $model = $this->getType($propDefinition['$ref']) . '::class'; - } elseif (isset($propDefinition['enum'])) { - $enum = 'Enum\\' . ucfirst($this->kebabCaseToCamelCase($type .'-'. $name)) . '::class'; + } elseif (isset($propDefinition['enum'])) { + $enum = 'Enum\\' . ucfirst($this->kebabCaseToCamelCase($type . '-' . $name)) . '::class'; } else { // TODO create exception class for this one throw new \Exception('Unknown property definition'); @@ -172,7 +171,7 @@ protected function generateSchema(string $type, array $modelSchema, array &$code protected function generateFields(string $type, array $modelSchema, array &$code): void { foreach ($modelSchema['properties'] as $name => $propDefinition) { - if (isset($propDefinition['type']) && !isset($propDefinition['enum'])) { + if (isset($propDefinition['type']) && ! isset($propDefinition['enum'])) { $propType = static::$propTypeMapping[$propDefinition['type']]; if ($propType == 'array' && isset($propDefinition['items']['$ref'])) { $propType = $this->getType($propDefinition['items']['$ref']) . '[]'; @@ -180,7 +179,7 @@ protected function generateFields(string $type, array $modelSchema, array &$code } elseif (isset($propDefinition['$ref'])) { $propType = $this->getType($propDefinition['$ref']); } elseif (isset($propDefinition['enum'])) { - $propType = 'Enum\\' . ucfirst($this->kebabCaseToCamelCase($type .'-'. $name)); + $propType = 'Enum\\' . ucfirst($this->kebabCaseToCamelCase($type . '-' . $name)); } else { // TODO create exception class for this one throw new \Exception('Unknown property definition'); @@ -317,7 +316,6 @@ protected function generateMonoFieldAccessors(array $modelSchema, array &$code): } - protected function getType(string $ref): string { //strip #/components/schemas/ @@ -367,7 +365,7 @@ protected function getFieldsWithMonoFieldModelType(array $modelSchema): array $propType = $propDefinition['type']; } - if (!isset($this->specs['components']['schemas'][$propType])) { + if (! isset($this->specs['components']['schemas'][$propType])) { continue; } @@ -403,7 +401,7 @@ protected function kebabCaseToCamelCase(string $name): string $name = str_replace(' ', '-', $name); $nameElems = explode('-', $name); - for ($i=1; $i 'https://api.bol.com/retailer/public/apispec/Retailer%20API%20-%20v10', + 'target' => 'retailer.json', + ], + [ + 'source' => 'https://api.bol.com/retailer/public/apispec/Shared%20API%20-%20v10', + 'target' => 'shared.json', + ], + ]; + + public static function run(): void + { + foreach (static::SPECS as $spec) { + $sourceFile = file_get_contents($spec['source']); + + // Tidy JSON formatting + $sourceTidied = json_encode(json_decode($sourceFile), JSON_PRETTY_PRINT + JSON_UNESCAPED_SLASHES + JSON_UNESCAPED_UNICODE); + + file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . $spec['target'], $sourceTidied); + } + } +} diff --git a/src/OpenApi/retailer.json b/src/OpenApi/retailer.json index 2e934d1..d6d4757 100644 --- a/src/OpenApi/retailer.json +++ b/src/OpenApi/retailer.json @@ -2133,8 +2133,8 @@ } } }, - "400": { - "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", + "404": { + "description": "Not found: The requested item could not be found.", "content": { "application/vnd.retailer.v10+json": { "schema": { @@ -2143,8 +2143,8 @@ } } }, - "404": { - "description": "Not found: The requested item could not be found.", + "400": { + "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", "content": { "application/vnd.retailer.v10+json": { "schema": { @@ -2354,8 +2354,8 @@ } } }, - "400": { - "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", + "404": { + "description": "Not found: The requested item could not be found.", "content": { "application/vnd.retailer.v10+json": { "schema": { @@ -2364,8 +2364,8 @@ } } }, - "404": { - "description": "Not found: The requested item could not be found.", + "400": { + "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", "content": { "application/vnd.retailer.v10+json": { "schema": { @@ -2773,8 +2773,8 @@ } } }, - "400": { - "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", + "404": { + "description": "Not found: The requested item could not be found.", "content": { "application/vnd.retailer.v10+pdf": { "schema": { @@ -2783,8 +2783,8 @@ } } }, - "404": { - "description": "Not found: The requested item could not be found.", + "400": { + "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", "content": { "application/vnd.retailer.v10+pdf": { "schema": { @@ -2827,8 +2827,8 @@ } } }, - "400": { - "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", + "404": { + "description": "Not found: The requested item could not be found.", "content": { "application/vnd.retailer.v10+json": { "schema": { @@ -2837,8 +2837,8 @@ } } }, - "404": { - "description": "Not found: The requested item could not be found.", + "400": { + "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", "content": { "application/vnd.retailer.v10+json": { "schema": { @@ -2948,8 +2948,8 @@ } } }, - "400": { - "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", + "404": { + "description": "Not found: The requested item could not be found.", "content": { "application/vnd.retailer.v10+pdf": { "schema": { @@ -2958,8 +2958,8 @@ } } }, - "404": { - "description": "Not found: The requested item could not be found.", + "400": { + "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", "content": { "application/vnd.retailer.v10+pdf": { "schema": { @@ -3003,8 +3003,8 @@ } } }, - "400": { - "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", + "404": { + "description": "Not found: The requested item could not be found.", "content": { "application/vnd.retailer.v10+pdf": { "schema": { @@ -3013,8 +3013,8 @@ } } }, - "404": { - "description": "Not found: The requested item could not be found.", + "400": { + "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", "content": { "application/vnd.retailer.v10+pdf": { "schema": { @@ -3496,7 +3496,7 @@ ], "requestBody": { "content": { - "multipart/form-data": { + "application/json": { "schema": { "required": [ "invoice" @@ -3843,7 +3843,7 @@ "tags": [ "Subscriptions" ], - "summary": "Retrieve public keys for push notification signature validation. (BETA)", + "summary": "Retrieve public keys for push notification signature validation.", "description": "Retrieve a list of public keys that should be used to validate the signature header for push notifications received from bol.com.", "operationId": "get-subscription-keys", "responses": { @@ -3865,7 +3865,7 @@ "tags": [ "Subscriptions" ], - "summary": "Send test push notification for subscriptions (BETA)", + "summary": "Send test push notification for subscriptions", "description": "Send a test push notification to all subscriptions for the provided event.", "operationId": "post-test-push-notification", "parameters": [ @@ -3935,8 +3935,8 @@ } } }, - "400": { - "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", + "404": { + "description": "Not found: The requested item could not be found.", "content": { "application/vnd.retailer.v10+json": { "schema": { @@ -3945,8 +3945,8 @@ } } }, - "404": { - "description": "Not found: The requested item could not be found.", + "400": { + "description": "Bad request: The sent request does not meet the API specification. Please check the error message(s) for more information.", "content": { "application/vnd.retailer.v10+json": { "schema": { @@ -4013,7 +4013,7 @@ "tags": [ "Subscriptions" ], - "summary": "Remove Event Notification Subscription (BETA)", + "summary": "Remove Event Notification Subscription", "description": "Deletes a specific event notification subscription associated with a retailer.", "operationId": "delete-push-notification-subscription", "parameters": [ @@ -4333,9 +4333,11 @@ "resources": { "type": "array", "description": "Array of event types for which the subscription is set. Note that some event types are only available for certain subscription types.", + "example": "['PROCESS_STATUS']", "items": { "type": "string", "description": "Array of event types for which the subscription is set. Note that some event types are only available for certain subscription types.", + "example": "['PROCESS_STATUS']", "enum": [ "PROCESS_STATUS", "SHIPMENT", @@ -4358,6 +4360,11 @@ "WEBHOOK", "GCP_PUBSUB" ] + }, + "enabled": { + "type": "boolean", + "description": "Whether the subscription is enabled and will receive notifications or not. Defaults to true.", + "example": true } } }, @@ -6019,6 +6026,7 @@ "items": { "type": "string", "description": "Array of event types for which the subscription is set. Note that some event types are only available for certain subscription types.", + "example": "['PROCESS_STATUS']", "enum": [ "TEST", "PROCESS_STATUS", @@ -6042,6 +6050,11 @@ "WEBHOOK", "GCP_PUBSUB" ] + }, + "enabled": { + "type": "boolean", + "description": "Whether the subscription is enabled and will receive notifications or not. Defaults to true.", + "example": true } } }, @@ -7213,9 +7226,12 @@ }, "RetailerInformationResponse": { "required": [ + "companyName", "displayName", + "kvkNumber", "registrationDate", - "retailerId" + "retailerId", + "vatNumber" ], "type": "object", "properties": { @@ -7227,7 +7243,22 @@ "displayName": { "type": "string", "description": "The name of the retailer visible on bol.com", - "example": "015E1DDD2EEB34009A616820E5429C" + "example": "RETAILER123" + }, + "companyName": { + "type": "string", + "description": "The company name of the retailer.", + "example": "Fietsentuin" + }, + "vatNumber": { + "type": "string", + "description": "The VAT number of the retailer.", + "example": "NL123456789B01" + }, + "kvkNumber": { + "type": "string", + "description": "The KVK number of the retailer.", + "example": "90005414" }, "registrationDate": { "type": "string", @@ -10199,4 +10230,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/OpenApi/shared.json b/src/OpenApi/shared.json index 0ed6132..ac21916 100644 --- a/src/OpenApi/shared.json +++ b/src/OpenApi/shared.json @@ -27,7 +27,7 @@ "tags": [ { "name": "Process Status", - "description": "Process status resources" + "description": "Process status resource" } ], "paths": { @@ -475,4 +475,4 @@ } } } -} +} \ No newline at end of file