diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..1281f04c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +/.github export-ignore +/tests export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.php_cs.dist export-ignore +docker-compose.yml export-ignore +Makefile export-ignore +phpunit.xml.dist export-ignore +phpunit11.xml.dist export-ignore +yarn.lock export-ignore diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 607c866e..3169321e 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -20,10 +20,12 @@ jobs: - "7.4" - "8.0" - "8.1" + - "8.3" + - "8.4" dependencies: - "lowest" - "highest" - symfony-yaml: ['^3.4', '^4', '^5', '^6'] + symfony-yaml: ['^3.4', '^4', '^5', '^6', '^7', '^8'] include: - os: "windows-latest" php: "8.0" @@ -46,11 +48,45 @@ jobs: symfony-yaml: '^6' - php: '7.4' symfony-yaml: '^6' + # symfony/yaml v7 does not run on PHP 7.* + - php: '7.1' + symfony-yaml: '^7' + - php: '7.2' + symfony-yaml: '^7' + - php: '7.3' + symfony-yaml: '^7' + - php: '7.4' + symfony-yaml: '^7' + # symfony/yaml v7 does not run on PHP 8.0 & 8.1 + - php: '8.0' + symfony-yaml: '^7' + - php: '8.1' + symfony-yaml: '^7' + # symfony/yaml v8 does not run on PHP 7.* + - php: '7.1' + symfony-yaml: '^8' + - php: '7.2' + symfony-yaml: '^8' + - php: '7.3' + symfony-yaml: '^8' + - php: '7.4' + symfony-yaml: '^8' + # symfony/yaml v8 does not run on PHP < 8.4 + - php: '8.0' + symfony-yaml: '^8' + - php: '8.1' + symfony-yaml: '^8' + - php: '8.3' + symfony-yaml: '^8' # symfony/yaml v3.4 is not compatible with PHP 8.0 but has no upper-bound, so it installs on it - php: '8.0' symfony-yaml: '^3.4' - php: '8.1' symfony-yaml: '^3.4' + - php: '8.3' + symfony-yaml: '^3.4' + - php: '8.4' + symfony-yaml: '^3.4' runs-on: ${{ matrix.os }} @@ -71,6 +107,10 @@ jobs: run: "composer require phpunit/phpunit '^9.5' --dev --no-interaction --ansi --no-install" if: matrix.php == '8.1' + - name: Require newer phpunit/phpunit version + run: "composer require phpunit/phpunit '^11.4' --dev --no-interaction --ansi --no-install" + if: matrix.php >= '8.3' + - name: "Install dependencies with Composer" uses: "ramsey/composer-install@v2" with: @@ -81,6 +121,11 @@ jobs: - name: PHPUnit tests run: make test + if: matrix.php < '8.3' + + - name: PHPUnit tests + run: make test-debug + if: matrix.php >= '8.3' - name: Code coverage run: make coverage diff --git a/.gitignore b/.gitignore index c86af6fe..795d780f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,6 @@ /.php_cs.cache /.phpunit.result.cache - +/.idea +xdebug_remote.log php-cs-fixer.phar diff --git a/Makefile b/Makefile old mode 100644 new mode 100755 index ee99b2ac..ca2b51b3 --- a/Makefile +++ b/Makefile @@ -11,8 +11,8 @@ DOCKER_PHP= DOCKER_NODE= IN_DOCKER=0 ifeq ($(IN_DOCKER),1) -DOCKER_PHP=docker-compose run --rm php -DOCKER_NODE=docker-compose run --rm -w /app node +DOCKER_PHP=docker compose run --rm php +DOCKER_NODE=docker compose run --rm -w /app node endif all: @@ -46,6 +46,11 @@ test: unit test-recursion.json test-recursion2.yaml test-recursion3_index.yaml t unit: $(DOCKER_PHP) php $(PHPARGS) $(XPHPARGS) vendor/bin/phpunit --verbose --colors=always $(TESTCASE) +test-debug: unit-debug test-recursion.json test-recursion2.yaml test-recursion3_index.yaml test-empty-maps.json + +unit-debug: + $(DOCKER_PHP) php $(PHPARGS) $(XPHPARGS) vendor/bin/phpunit --debug --testdox --colors=always -c phpunit11.xml.dist $(TESTCASE) + # test specific JSON files in tests/spec/data/ # e.g. test-recursion will run validation on tests/spec/data/recursion.json test-%: tests/spec/data/% @@ -62,13 +67,6 @@ lint: install stan: $(DOCKER_PHP) php $(PHPARGS) vendor/bin/phpstan analyse -l 5 src -# copy openapi3 json schema -schemas/openapi-v3.0.json: vendor/oai/openapi-specification/schemas/v3.0/schema.json - cp $< $@ - -schemas/openapi-v3.0.yaml: vendor/oai/openapi-specification/schemas/v3.0/schema.yaml - cp $< $@ - php-cs-fixer.phar: wget -q https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.16.7/php-cs-fixer.phar && chmod +x php-cs-fixer.phar diff --git a/README.md b/README.md index 3d25df05..abcee183 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,26 @@ +## Atention +This is a fork of [cebe/php-openapi](https://github.com/cebe/php-openapi). I created it as library because the pull request +of the openapi3.1 was taking years and a lot of developers want to use it. + # php-openapi -Read and write [OpenAPI](https://www.openapis.org/) 3.0.x YAML and JSON files and make the content accessible in PHP objects. +Read and write [OpenAPI](https://www.openapis.org/) 3.x YAML and JSON files and make the content accessible in PHP objects. + +It also provides a CLI tool for validating and converting OpenAPI 3.x Description files. + +Supported OpenAPI versions: -It also provides a CLI tool for validating and converting OpenAPI 3.0.x Description files. +- 3.0.x +- 3.1.x -[![Latest Stable Version](https://poser.pugx.org/cebe/php-openapi/v/stable)](https://packagist.org/packages/cebe/php-openapi) -[![Total Downloads](https://poser.pugx.org/cebe/php-openapi/downloads)](https://packagist.org/packages/cebe/php-openapi) -[![Build Status](https://github.com/cebe/php-openapi/workflows/CI/badge.svg)](https://github.com/cebe/php-openapi/actions) +[![Latest Stable Version](https://poser.pugx.org/devizzent/cebe-php-openapi/v/stable)](https://packagist.org/packages/devizzent/cebe-php-openapi) +[![Total Downloads](https://poser.pugx.org/devizzent/cebe-php-openapi/downloads)](https://packagist.org/packages/devizzent/cebe-php-openapi) +[![Build Status](https://github.com/devizzent/cebe-php-openapi/workflows/CI/badge.svg)](https://github.com/devizzent/cebe-php-openapi/actions) ## Install - composer require cebe/php-openapi + composer require devizzent/cebe-php-openapi ## Requirements @@ -22,11 +31,7 @@ It also provides a CLI tool for validating and converting OpenAPI 3.0.x Descript This library provides a low level API for reading and writing OpenAPI files. It is used by higher level tools to do awesome work: -- [cebe/yii2-openapi](https://github.com/cebe/yii2-openapi) Code Generator for REST API from OpenAPI 3 Descriptions, includes fake data generator. -- [cebe/yii2-app-api](https://github.com/cebe/yii2-app-api) Yii framework application template for developing API-first applications. -- [league/openapi-psr7-validator](https://github.com/thephpleague/openapi-psr7-validator) validates PSR-7 messages (HTTP request/response) against OpenAPI descriptions. -- [dsuurlant/response2schema](https://github.com/dsuurlant/response2schema) a quick and easy tool for generating OpenAPI schemas based on example data. -- ... ([add yours](https://github.com/cebe/php-openapi/edit/master/README.md#L24)) +- ... ([add yours](https://github.com/devizzent/cebe-php-openapi/edit/master/README.md#L24)) ## Usage @@ -222,7 +227,7 @@ $openapi->resolveReferences( The library provides simple validation operations, that check basic OpenAPI spec requirements. This is the same as "structural errors found while reading the API Description file" from the CLI tool. -This validation does not include checking against the OpenAPI v3.0 JSON schema, this is only implemented in the CLI. +This validation does not include checking against the OpenAPI v3.0/v3.1 JSON schemas, this is only implemented in the CLI. ``` // return `true` in case no errors have been found, `false` in case of errors. @@ -235,48 +240,6 @@ $errors = $openapi->getErrors(); > but the list of errors given may not be complete. Also a passing validation does not necessarily indicate a completely > valid spec. - -## Completeness - -This library is currently work in progress, the following list tracks completeness: - -- [x] read OpenAPI 3.0 JSON -- [x] read OpenAPI 3.0 YAML -- [ ] OpenAPI 3.0 Schema - - [x] OpenAPI Object - - [x] Info Object - - [x] Contact Object - - [x] License Object - - [x] Server Object - - [x] Server Variable Object - - [x] Components Object - - [x] Paths Object - - [x] Path Item Object - - [x] Operation Object - - [x] External Documentation Object - - [x] Parameter Object - - [x] Request Body Object - - [x] Media Type Object - - [x] Encoding Object - - [x] Responses Object - - [x] Response Object - - [x] Callback Object - - [x] Example Object - - [x] Link Object - - [ ] [Runtime Expressions](https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#runtime-expressions) - - [x] Header Object - - [x] Tag Object - - [x] Reference Object - - [x] Schema Object - - [x] load/read - - [ ] validation - - [x] Discriminator Object - - [x] XML Object - - [x] Security Scheme Object - - [x] OAuth Flows Object - - [x] OAuth Flow Object - - [x] Security Requirement Object - # Development You may use the docker environment for local development: @@ -286,13 +249,3 @@ You may use the docker environment for local development: make IN_DOCKER=1 test ... - -# Support - -**Need help with your API project?** - -Professional support, consulting as well as software development services are available: - -https://www.cebe.cc/en/contact - -Development of this library is sponsored by [cebe.:cloud: "Your Professional Deployment Platform"](https://cebe.cloud). diff --git a/bin/php-openapi b/bin/php-openapi index ae2ffdc8..53e5196d 100755 --- a/bin/php-openapi +++ b/bin/php-openapi @@ -131,16 +131,22 @@ switch ($command) { // Validate + // OpenAPI version check + $openApiVersion = $openApi->getMajorVersion(); + if ($openApiVersion === \cebe\openapi\spec\OpenApi::VERSION_UNSUPPORTED) { + error("Unsupported OpenAPI version: " . $openApi->openapi); + } + $openApi->validate(); $errors = array_merge($errors, $openApi->getErrors()); $validator = new JsonSchema\Validator; $openApiData = $openApi->getSerializableData(); - $validator->validate($openApiData, (object)['$ref' => 'file://' . dirname(__DIR__) . '/schemas/openapi-v3.0.json']); + $validator->validate($openApiData, (object)['$ref' => 'file://' . dirname(__DIR__) . "/schemas/openapi-v{$openApiVersion}.json"]); if ($validator->isValid() && empty($errors)) { if(!$silentMode) { - print_formatted("The supplied API Description \B\Gvalidates\C against the OpenAPI v3.0 schema.\n", STDERR); + print_formatted("The supplied API Description \B\Gvalidates\C against the OpenAPI v{$openApiVersion} schema.\n", STDERR); } exit(0); } @@ -163,7 +169,7 @@ switch ($command) { } } if (!$validator->isValid()) { - print_formatted("\BOpenAPI v3.0 schema violations:\C\n", STDERR); + print_formatted("\BOpenAPI v{$openApiVersion} schema violations:\C\n", STDERR); $errors = $validator->getErrors(); foreach ($errors as $error) { // hide some errors triggered by other errors further down the path diff --git a/composer.json b/composer.json old mode 100644 new mode 100755 index 46cb26f2..312c6454 --- a/composer.json +++ b/composer.json @@ -1,8 +1,8 @@ { - "name": "cebe/php-openapi", + "name": "devizzent/cebe-php-openapi", "description": "Read and write OpenAPI yaml/json files and make the content accessable in PHP objects.", "keywords": ["openapi"], - "homepage": "https://github.com/cebe/php-openapi#readme", + "homepage": "https://github.com/DEVizzent/cebe-php-openapi#readme", "type": "library", "license": "MIT", "authors": [ @@ -11,30 +11,38 @@ "email": "mail@cebe.cc", "homepage": "https://cebe.cc/", "role": "Creator" + }, + { + "name": "Vicent Valls", + "email": "vizzent@gmail.com" } ], "support": { - "issues": "https://github.com/cebe/php-openapi/issues", - "source": "https://github.com/cebe/php-openapi" + "issues": "https://github.com/DEVizzent/cebe-php-openapi/issues", + "source": "https://github.com/DEVizzent/cebe-php-openapi" }, "require": { "php": ">=7.1.0", "ext-json": "*", - "symfony/yaml": "^3.4 || ^4 || ^5 || ^6", - "justinrainbow/json-schema": "^5.2" + "symfony/yaml": "^3.4 || ^4 || ^5 || ^6 || ^7 || ^8", + "justinrainbow/json-schema": "^5.2 || ^6.0" }, "require-dev": { "cebe/indent": "*", - "phpunit/phpunit": "^6.5 || ^7.5 || ^8.5 || ^9.4", - "oai/openapi-specification": "3.0.3", + "phpunit/phpunit": "^6.5 || ^7.5 || ^8.5 || ^9.4 || ^11.4", + + "oai/openapi-specification-3.0": "3.0.3", + "mermade/openapi3-examples": "1.0.0", "apis-guru/openapi-directory": "1.0.0", - "nexmo/api-specification": "1.0.0", "phpstan/phpstan": "^0.12.0" }, "conflict": { "symfony/yaml": "3.4.0 - 3.4.4 || 4.0.0 - 4.4.17 || 5.0.0 - 5.1.9 || 5.2.0" }, + "replace": { + "cebe/php-openapi":"1.7.0" + }, "autoload": { "psr-4": { "cebe\\openapi\\": "src/" @@ -52,7 +60,7 @@ { "type": "package", "package": { - "name": "oai/openapi-specification", + "name": "oai/openapi-specification-3.0", "version": "3.0.3", "source": { "url": "https://github.com/OAI/OpenAPI-Specification", @@ -64,36 +72,35 @@ { "type": "package", "package": { - "name": "mermade/openapi3-examples", - "version": "1.0.0", + "name": "oai/openapi-specification-3.1", + "version": "3.1.0", "source": { - "url": "https://github.com/Mermade/openapi3-examples", + "url": "https://github.com/OAI/OpenAPI-Specification", "type": "git", - "reference": "3e8740c4994310a5d6a35d9b19e405862326f149" + "reference": "v3.1.1-dev" } } }, { "type": "package", "package": { - "name": "apis-guru/openapi-directory", + "name": "mermade/openapi3-examples", "version": "1.0.0", "source": { - "url": "https://github.com/APIs-guru/openapi-directory", + "url": "https://github.com/Mermade/openapi3-examples", "type": "git", - "reference": "openapi3.0.0" + "reference": "9c2997e1a25919a8182080cc43a4db06d2dc775d" } } }, { "type": "package", "package": { - "name": "nexmo/api-specification", + "name": "apis-guru/openapi-directory", "version": "1.0.0", - "source": { - "url": "https://github.com/Nexmo/api-specification", - "type": "git", - "reference": "voice-2.0.0" + "dist": { + "url": "https://github.com/APIs-guru/openapi-directory/archive/refs/heads/openapi3.0.0.zip", + "type": "zip" } } } diff --git a/docker-compose.yml b/docker-compose.yml index 914efb0f..e84e636c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,12 +8,16 @@ services: - ./tests/tmp/.composer:/root/.composer:rw - .:/app environment: + - PHP_IDE_CONFIG='serverName=host.docker.internal' + - XDEBUG_TRIGGER=1 - TZ=UTC - TIMEZONE=UTC - IN_DOCKER=docker - PHP_XDEBUG_ENABLED=1 - XDEBUG_CONFIG="remote_host=host.docker.internal" - PHP_IDE_CONFIG="serverName=Docker" + extra_hosts: + - "host.docker.internal:host-gateway" tty: true node: image: node:12 diff --git a/phpunit11.xml.dist b/phpunit11.xml.dist new file mode 100644 index 00000000..972a3f32 --- /dev/null +++ b/phpunit11.xml.dist @@ -0,0 +1,22 @@ + + + + + ./tests + + + + + ./src + + + ./vendor + ./tests + + + diff --git a/schemas/openapi-v3.1.json b/schemas/openapi-v3.1.json new file mode 100644 index 00000000..814ad78e --- /dev/null +++ b/schemas/openapi-v3.1.json @@ -0,0 +1,1382 @@ +{ + "$id": "https://spec.openapis.org/oas/3.1/schema/2021-09-28", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "openapi": { + "type": "string", + "pattern": "^3\\.1\\.\\d+(-.+)?$" + }, + "info": { + "$ref": "#/$defs/info" + }, + "jsonSchemaDialect": { + "type": "string", + "format": "uri", + "default": "https://spec.openapis.org/oas/3.1/dialect/base" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + }, + "paths": { + "$ref": "#/$defs/paths" + }, + "webhooks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + }, + "components": { + "$ref": "#/$defs/components" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/$defs/tag" + } + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "openapi", + "info" + ], + "anyOf": [ + { + "required": [ + "paths" + ] + }, + { + "required": [ + "components" + ] + }, + { + "required": [ + "webhooks" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "info": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#info-object", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "termsOfService": { + "type": "string" + }, + "contact": { + "$ref": "#/$defs/contact" + }, + "license": { + "$ref": "#/$defs/license" + }, + "version": { + "type": "string" + } + }, + "required": [ + "title", + "version" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "contact": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#contact-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + }, + "email": { + "type": "string" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "license": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#license-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "name" + ], + "oneOf": [ + { + "required": [ + "identifier" + ] + }, + { + "required": [ + "url" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-object", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "variables": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/server-variable" + } + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server-variable": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-variable-object", + "type": "object", + "properties": { + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "default": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "default" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "components": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#components-object", + "type": "object", + "properties": { + "schemas": { + "type": "object", + "additionalProperties": { + "$dynamicRef": "#meta" + } + }, + "responses": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/response-or-reference" + } + }, + "parameters": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + }, + "requestBodies": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/request-body-or-reference" + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "securitySchemes": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/security-scheme-or-reference" + } + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "pathItems": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + } + }, + "patternProperties": { + "^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$": { + "$comment": "Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected", + "propertyNames": { + "pattern": "^[a-zA-Z0-9._-]+$" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "paths": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#paths-object", + "type": "object", + "patternProperties": { + "^/": { + "$ref": "#/$defs/path-item" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#path-item-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + } + }, + "patternProperties": { + "^(get|put|post|delete|options|head|patch|trace)$": { + "$ref": "#/$defs/operation" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/path-item" + } + }, + "operation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#operation-object", + "type": "object", + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "requestBody": { + "$ref": "#/$defs/request-body-or-reference" + }, + "responses": { + "$ref": "#/$defs/responses" + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "external-documentation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#external-documentation-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#parameter-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "path", + "cookie" + ] + }, + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "allowEmptyValue": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "required": [ + "name", + "in" + ], + "oneOf": [ + { + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "type": "string" + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + }, + "allOf": [ + { + "$ref": "#/$defs/examples" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-form" + } + ], + "$defs": { + "styles-for-path": { + "if": { + "properties": { + "in": { + "const": "path" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "name": { + "pattern": "[^/#?]+$" + }, + "style": { + "default": "simple", + "enum": [ + "matrix", + "label", + "simple" + ] + }, + "required": { + "const": true + } + }, + "required": [ + "required" + ] + } + }, + "styles-for-header": { + "if": { + "properties": { + "in": { + "const": "header" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + } + } + } + }, + "styles-for-query": { + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + } + } + } + }, + "styles-for-cookie": { + "if": { + "properties": { + "in": { + "const": "cookie" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "const": "form" + } + } + } + }, + "styles-for-form": { + "if": { + "properties": { + "style": { + "const": "form" + } + }, + "required": [ + "style" + ] + }, + "then": { + "properties": { + "explode": { + "default": true + } + } + }, + "else": { + "properties": { + "explode": { + "default": false + } + } + } + } + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/parameter" + } + }, + "request-body": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#request-body-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "content": { + "$ref": "#/$defs/content" + }, + "required": { + "default": false, + "type": "boolean" + } + }, + "required": [ + "content" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "request-body-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/request-body" + } + }, + "content": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#fixed-fields-10", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/media-type" + }, + "propertyNames": { + "format": "media-range" + } + }, + "media-type": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#media-type-object", + "type": "object", + "properties": { + "schema": { + "$dynamicRef": "#meta" + }, + "encoding": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/encoding" + } + } + }, + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/examples" + } + ], + "unevaluatedProperties": false + }, + "encoding": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#encoding-object", + "type": "object", + "properties": { + "contentType": { + "type": "string", + "format": "media-range" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + }, + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/encoding/$defs/explode-default" + } + ], + "unevaluatedProperties": false, + "$defs": { + "explode-default": { + "if": { + "properties": { + "style": { + "const": "form" + } + }, + "required": [ + "style" + ] + }, + "then": { + "properties": { + "explode": { + "default": true + } + } + }, + "else": { + "properties": { + "explode": { + "default": false + } + } + } + } + } + }, + "responses": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#responses-object", + "type": "object", + "properties": { + "default": { + "$ref": "#/$defs/response-or-reference" + } + }, + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": { + "$ref": "#/$defs/response-or-reference" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "response": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#response-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "content": { + "$ref": "#/$defs/content" + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + } + }, + "required": [ + "description" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "response-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/response" + } + }, + "callbacks": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#callback-object", + "type": "object", + "$ref": "#/$defs/specification-extensions", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + }, + "callbacks-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/callbacks" + } + }, + "example": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#example-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "value": true, + "externalValue": { + "type": "string", + "format": "uri" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "example-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/example" + } + }, + "link": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#link-object", + "type": "object", + "properties": { + "operationRef": { + "type": "string", + "format": "uri-reference" + }, + "operationId": true, + "parameters": { + "$ref": "#/$defs/map-of-strings" + }, + "requestBody": true, + "description": { + "type": "string" + }, + "body": { + "$ref": "#/$defs/server" + } + }, + "oneOf": [ + { + "required": [ + "operationRef" + ] + }, + { + "required": [ + "operationId" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "link-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/link" + } + }, + "header": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#header-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "oneOf": [ + { + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + }, + "explode": { + "default": false, + "type": "boolean" + } + }, + "$ref": "#/$defs/examples" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "header-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/header" + } + }, + "tag": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#tag-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "name" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "reference": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#reference-object", + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "unevaluatedProperties": false + }, + "schema": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#schema-object", + "$dynamicAnchor": "meta", + "type": [ + "object", + "boolean" + ] + }, + "security-scheme": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-scheme-object", + "type": "object", + "properties": { + "type": { + "enum": [ + "apiKey", + "http", + "mutualTLS", + "oauth2", + "openIdConnect" + ] + }, + "description": { + "type": "string" + } + }, + "required": [ + "type" + ], + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-apikey" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http-bearer" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oauth2" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oidc" + } + ], + "unevaluatedProperties": false, + "$defs": { + "type-apikey": { + "if": { + "properties": { + "type": { + "const": "apiKey" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "cookie" + ] + } + }, + "required": [ + "name", + "in" + ] + } + }, + "type-http": { + "if": { + "properties": { + "type": { + "const": "http" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "scheme": { + "type": "string" + } + }, + "required": [ + "scheme" + ] + } + }, + "type-http-bearer": { + "if": { + "properties": { + "type": { + "const": "http" + }, + "scheme": { + "type": "string", + "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" + } + }, + "required": [ + "type", + "scheme" + ] + }, + "then": { + "properties": { + "bearerFormat": { + "type": "string" + } + } + } + }, + "type-oauth2": { + "if": { + "properties": { + "type": { + "const": "oauth2" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "flows": { + "$ref": "#/$defs/oauth-flows" + } + }, + "required": [ + "flows" + ] + } + }, + "type-oidc": { + "if": { + "properties": { + "type": { + "const": "openIdConnect" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "openIdConnectUrl": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "openIdConnectUrl" + ] + } + } + } + }, + "security-scheme-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/security-scheme" + } + }, + "oauth-flows": { + "type": "object", + "properties": { + "implicit": { + "$ref": "#/$defs/oauth-flows/$defs/implicit" + }, + "password": { + "$ref": "#/$defs/oauth-flows/$defs/password" + }, + "clientCredentials": { + "$ref": "#/$defs/oauth-flows/$defs/client-credentials" + }, + "authorizationCode": { + "$ref": "#/$defs/oauth-flows/$defs/authorization-code" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "implicit": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string" + }, + "refreshUrl": { + "type": "string" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "password": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string" + }, + "refreshUrl": { + "type": "string" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "client-credentials": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string" + }, + "refreshUrl": { + "type": "string" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "authorization-code": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string" + }, + "tokenUrl": { + "type": "string" + }, + "refreshUrl": { + "type": "string" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + } + } + }, + "security-requirement": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-requirement-object", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "specification-extensions": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#specification-extensions", + "patternProperties": { + "^x-": true + } + }, + "examples": { + "properties": { + "example": true, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + } + } + }, + "map-of-strings": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } +} diff --git a/schemas/openapi-v3.1.yaml b/schemas/openapi-v3.1.yaml new file mode 100644 index 00000000..1943af86 --- /dev/null +++ b/schemas/openapi-v3.1.yaml @@ -0,0 +1,952 @@ +$id: 'https://spec.openapis.org/oas/3.1/schema/2021-09-28' +$schema: 'https://json-schema.org/draft/2020-12/schema' + +type: object +properties: + openapi: + type: string + pattern: '^3\.1\.\d+(-.+)?$' + info: + $ref: '#/$defs/info' + jsonSchemaDialect: + type: string + format: uri + default: 'https://spec.openapis.org/oas/3.1/dialect/base' + servers: + type: array + items: + $ref: '#/$defs/server' + paths: + $ref: '#/$defs/paths' + webhooks: + type: object + additionalProperties: + $ref: '#/$defs/path-item-or-reference' + components: + $ref: '#/$defs/components' + security: + type: array + items: + $ref: '#/$defs/security-requirement' + tags: + type: array + items: + $ref: '#/$defs/tag' + externalDocs: + $ref: '#/$defs/external-documentation' +required: + - openapi + - info +anyOf: + - required: + - paths + - required: + - components + - required: + - webhooks +$ref: '#/$defs/specification-extensions' +unevaluatedProperties: false + +$defs: + info: + $comment: https://spec.openapis.org/oas/v3.1.0#info-object + type: object + properties: + title: + type: string + summary: + type: string + description: + type: string + termsOfService: + type: string + contact: + $ref: '#/$defs/contact' + license: + $ref: '#/$defs/license' + version: + type: string + required: + - title + - version + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + contact: + $comment: https://spec.openapis.org/oas/v3.1.0#contact-object + type: object + properties: + name: + type: string + url: + type: string + email: + type: string + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + license: + $comment: https://spec.openapis.org/oas/v3.1.0#license-object + type: object + properties: + name: + type: string + identifier: + type: string + url: + type: string + format: uri + required: + - name + oneOf: + - required: + - identifier + - required: + - url + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + server: + $comment: https://spec.openapis.org/oas/v3.1.0#server-object + type: object + properties: + url: + type: string + description: + type: string + variables: + type: object + additionalProperties: + $ref: '#/$defs/server-variable' + required: + - url + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + server-variable: + $comment: https://spec.openapis.org/oas/v3.1.0#server-variable-object + type: object + properties: + enum: + type: array + items: + type: string + minItems: 1 + default: + type: string + description: + type: string + required: + - default + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + components: + $comment: https://spec.openapis.org/oas/v3.1.0#components-object + type: object + properties: + schemas: + type: object + additionalProperties: + $dynamicRef: '#meta' + responses: + type: object + additionalProperties: + $ref: '#/$defs/response-or-reference' + parameters: + type: object + additionalProperties: + $ref: '#/$defs/parameter-or-reference' + examples: + type: object + additionalProperties: + $ref: '#/$defs/example-or-reference' + requestBodies: + type: object + additionalProperties: + $ref: '#/$defs/request-body-or-reference' + headers: + type: object + additionalProperties: + $ref: '#/$defs/header-or-reference' + securitySchemes: + type: object + additionalProperties: + $ref: '#/$defs/security-scheme-or-reference' + links: + type: object + additionalProperties: + $ref: '#/$defs/link-or-reference' + callbacks: + type: object + additionalProperties: + $ref: '#/$defs/callbacks-or-reference' + pathItems: + type: object + additionalProperties: + $ref: '#/$defs/path-item-or-reference' + patternProperties: + '^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$': + $comment: Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected + propertyNames: + pattern: '^[a-zA-Z0-9._-]+$' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + paths: + $comment: https://spec.openapis.org/oas/v3.1.0#paths-object + type: object + patternProperties: + '^/': + $ref: '#/$defs/path-item' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + path-item: + $comment: https://spec.openapis.org/oas/v3.1.0#path-item-object + type: object + properties: + summary: + type: string + description: + type: string + servers: + type: array + items: + $ref: '#/$defs/server' + parameters: + type: array + items: + $ref: '#/$defs/parameter-or-reference' + patternProperties: + '^(get|put|post|delete|options|head|patch|trace)$': + $ref: '#/$defs/operation' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + path-item-or-reference: + if: + type: object + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/path-item' + + operation: + $comment: https://spec.openapis.org/oas/v3.1.0#operation-object + type: object + properties: + tags: + type: array + items: + type: string + summary: + type: string + description: + type: string + externalDocs: + $ref: '#/$defs/external-documentation' + operationId: + type: string + parameters: + type: array + items: + $ref: '#/$defs/parameter-or-reference' + requestBody: + $ref: '#/$defs/request-body-or-reference' + responses: + $ref: '#/$defs/responses' + callbacks: + type: object + additionalProperties: + $ref: '#/$defs/callbacks-or-reference' + deprecated: + default: false + type: boolean + security: + type: array + items: + $ref: '#/$defs/security-requirement' + servers: + type: array + items: + $ref: '#/$defs/server' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + external-documentation: + $comment: https://spec.openapis.org/oas/v3.1.0#external-documentation-object + type: object + properties: + description: + type: string + url: + type: string + format: uri + required: + - url + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + parameter: + $comment: https://spec.openapis.org/oas/v3.1.0#parameter-object + type: object + properties: + name: + type: string + in: + enum: + - query + - header + - path + - cookie + description: + type: string + required: + default: false + type: boolean + deprecated: + default: false + type: boolean + allowEmptyValue: + default: false + type: boolean + schema: + $dynamicRef: '#meta' + content: + $ref: '#/$defs/content' + minProperties: 1 + maxProperties: 1 + required: + - name + - in + oneOf: + - required: + - schema + - required: + - content + dependentSchemas: + schema: + properties: + style: + type: string + explode: + type: boolean + allowReserved: + default: false + type: boolean + allOf: + - $ref: '#/$defs/examples' + - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path' + - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header' + - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query' + - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie' + - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-form' + + $defs: + styles-for-path: + if: + properties: + in: + const: path + required: + - in + then: + properties: + name: + pattern: '[^/#?]+$' + style: + default: simple + enum: + - matrix + - label + - simple + required: + const: true + required: + - required + + styles-for-header: + if: + properties: + in: + const: header + required: + - in + then: + properties: + style: + default: simple + const: simple + + styles-for-query: + if: + properties: + in: + const: query + required: + - in + then: + properties: + style: + default: form + enum: + - form + - spaceDelimited + - pipeDelimited + - deepObject + + styles-for-cookie: + if: + properties: + in: + const: cookie + required: + - in + then: + properties: + style: + default: form + const: form + + styles-for-form: + if: + properties: + style: + const: form + required: + - style + then: + properties: + explode: + default: true + else: + properties: + explode: + default: false + + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + parameter-or-reference: + if: + type: object + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/parameter' + + request-body: + $comment: https://spec.openapis.org/oas/v3.1.0#request-body-object + type: object + properties: + description: + type: string + content: + $ref: '#/$defs/content' + required: + default: false + type: boolean + required: + - content + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + request-body-or-reference: + if: + type: object + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/request-body' + + content: + $comment: https://spec.openapis.org/oas/v3.1.0#fixed-fields-10 + type: object + additionalProperties: + $ref: '#/$defs/media-type' + propertyNames: + format: media-range + + media-type: + $comment: https://spec.openapis.org/oas/v3.1.0#media-type-object + type: object + properties: + schema: + $dynamicRef: '#meta' + encoding: + type: object + additionalProperties: + $ref: '#/$defs/encoding' + allOf: + - $ref: '#/$defs/specification-extensions' + - $ref: '#/$defs/examples' + unevaluatedProperties: false + + encoding: + $comment: https://spec.openapis.org/oas/v3.1.0#encoding-object + type: object + properties: + contentType: + type: string + format: media-range + headers: + type: object + additionalProperties: + $ref: '#/$defs/header-or-reference' + style: + default: form + enum: + - form + - spaceDelimited + - pipeDelimited + - deepObject + explode: + type: boolean + allowReserved: + default: false + type: boolean + allOf: + - $ref: '#/$defs/specification-extensions' + - $ref: '#/$defs/encoding/$defs/explode-default' + unevaluatedProperties: false + + $defs: + explode-default: + if: + properties: + style: + const: form + required: + - style + then: + properties: + explode: + default: true + else: + properties: + explode: + default: false + + responses: + $comment: https://spec.openapis.org/oas/v3.1.0#responses-object + type: object + properties: + default: + $ref: '#/$defs/response-or-reference' + patternProperties: + '^[1-5](?:[0-9]{2}|XX)$': + $ref: '#/$defs/response-or-reference' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + response: + $comment: https://spec.openapis.org/oas/v3.1.0#response-object + type: object + properties: + description: + type: string + headers: + type: object + additionalProperties: + $ref: '#/$defs/header-or-reference' + content: + $ref: '#/$defs/content' + links: + type: object + additionalProperties: + $ref: '#/$defs/link-or-reference' + required: + - description + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + response-or-reference: + if: + type: object + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/response' + + callbacks: + $comment: https://spec.openapis.org/oas/v3.1.0#callback-object + type: object + $ref: '#/$defs/specification-extensions' + additionalProperties: + $ref: '#/$defs/path-item-or-reference' + + callbacks-or-reference: + if: + type: object + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/callbacks' + + example: + $comment: https://spec.openapis.org/oas/v3.1.0#example-object + type: object + properties: + summary: + type: string + description: + type: string + value: true + externalValue: + type: string + format: uri + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + example-or-reference: + if: + type: object + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/example' + + link: + $comment: https://spec.openapis.org/oas/v3.1.0#link-object + type: object + properties: + operationRef: + type: string + format: uri-reference + operationId: true + parameters: + $ref: '#/$defs/map-of-strings' + requestBody: true + description: + type: string + body: + $ref: '#/$defs/server' + oneOf: + - required: + - operationRef + - required: + - operationId + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + link-or-reference: + if: + type: object + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/link' + + header: + $comment: https://spec.openapis.org/oas/v3.1.0#header-object + type: object + properties: + description: + type: string + required: + default: false + type: boolean + deprecated: + default: false + type: boolean + schema: + $dynamicRef: '#meta' + content: + $ref: '#/$defs/content' + minProperties: 1 + maxProperties: 1 + oneOf: + - required: + - schema + - required: + - content + dependentSchemas: + schema: + properties: + style: + default: simple + const: simple + explode: + default: false + type: boolean + $ref: '#/$defs/examples' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + header-or-reference: + if: + type: object + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/header' + + tag: + $comment: https://spec.openapis.org/oas/v3.1.0#tag-object + type: object + properties: + name: + type: string + description: + type: string + externalDocs: + $ref: '#/$defs/external-documentation' + required: + - name + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + reference: + $comment: https://spec.openapis.org/oas/v3.1.0#reference-object + type: object + properties: + $ref: + type: string + format: uri-reference + summary: + type: string + description: + type: string + unevaluatedProperties: false + + schema: + $comment: https://spec.openapis.org/oas/v3.1.0#schema-object + $dynamicAnchor: meta + type: + - object + - boolean + + security-scheme: + $comment: https://spec.openapis.org/oas/v3.1.0#security-scheme-object + type: object + properties: + type: + enum: + - apiKey + - http + - mutualTLS + - oauth2 + - openIdConnect + description: + type: string + required: + - type + allOf: + - $ref: '#/$defs/specification-extensions' + - $ref: '#/$defs/security-scheme/$defs/type-apikey' + - $ref: '#/$defs/security-scheme/$defs/type-http' + - $ref: '#/$defs/security-scheme/$defs/type-http-bearer' + - $ref: '#/$defs/security-scheme/$defs/type-oauth2' + - $ref: '#/$defs/security-scheme/$defs/type-oidc' + unevaluatedProperties: false + + $defs: + type-apikey: + if: + properties: + type: + const: apiKey + required: + - type + then: + properties: + name: + type: string + in: + enum: + - query + - header + - cookie + required: + - name + - in + + type-http: + if: + properties: + type: + const: http + required: + - type + then: + properties: + scheme: + type: string + required: + - scheme + + type-http-bearer: + if: + properties: + type: + const: http + scheme: + type: string + pattern: ^[Bb][Ee][Aa][Rr][Ee][Rr]$ + required: + - type + - scheme + then: + properties: + bearerFormat: + type: string + + type-oauth2: + if: + properties: + type: + const: oauth2 + required: + - type + then: + properties: + flows: + $ref: '#/$defs/oauth-flows' + required: + - flows + + type-oidc: + if: + properties: + type: + const: openIdConnect + required: + - type + then: + properties: + openIdConnectUrl: + type: string + format: uri + required: + - openIdConnectUrl + + security-scheme-or-reference: + if: + type: object + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/security-scheme' + + oauth-flows: + type: object + properties: + implicit: + $ref: '#/$defs/oauth-flows/$defs/implicit' + password: + $ref: '#/$defs/oauth-flows/$defs/password' + clientCredentials: + $ref: '#/$defs/oauth-flows/$defs/client-credentials' + authorizationCode: + $ref: '#/$defs/oauth-flows/$defs/authorization-code' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + $defs: + implicit: + type: object + properties: + authorizationUrl: + type: string + refreshUrl: + type: string + scopes: + $ref: '#/$defs/map-of-strings' + required: + - authorizationUrl + - scopes + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + password: + type: object + properties: + tokenUrl: + type: string + refreshUrl: + type: string + scopes: + $ref: '#/$defs/map-of-strings' + required: + - tokenUrl + - scopes + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + client-credentials: + type: object + properties: + tokenUrl: + type: string + refreshUrl: + type: string + scopes: + $ref: '#/$defs/map-of-strings' + required: + - tokenUrl + - scopes + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + authorization-code: + type: object + properties: + authorizationUrl: + type: string + tokenUrl: + type: string + refreshUrl: + type: string + scopes: + $ref: '#/$defs/map-of-strings' + required: + - authorizationUrl + - tokenUrl + - scopes + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + security-requirement: + $comment: https://spec.openapis.org/oas/v3.1.0#security-requirement-object + type: object + additionalProperties: + type: array + items: + type: string + + specification-extensions: + $comment: https://spec.openapis.org/oas/v3.1.0#specification-extensions + patternProperties: + '^x-': true + + examples: + properties: + example: true + examples: + type: object + additionalProperties: + $ref: '#/$defs/example-or-reference' + + map-of-strings: + type: object + additionalProperties: + type: string diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 1de429bd..7a41828f 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -86,22 +86,22 @@ public function __construct(array $data) } else { // array $this->_properties[$property] = []; - foreach ($data[$property] as $item) { + foreach ($data[$property] as $key => $item) { if ($type[0] === Type::STRING) { if (!is_string($item)) { $this->_errors[] = "property '$property' must be array of strings, but array has " . gettype($item) . " element."; } - $this->_properties[$property][] = $item; + $this->_properties[$property][$key] = $item; } elseif (Type::isScalar($type[0])) { - $this->_properties[$property][] = $item; + $this->_properties[$property][$key] = $item; } elseif ($type[0] === Type::ANY) { if (is_array($item) && isset($item['$ref'])) { - $this->_properties[$property][] = new Reference($item, null); + $this->_properties[$property][$key] = new Reference($item, null); } else { - $this->_properties[$property][] = $item; + $this->_properties[$property][$key] = $item; } } else { - $this->_properties[$property][] = $this->instantiate($type[0], $item); + $this->_properties[$property][$key] = $this->instantiate($type[0], $item); } } } @@ -325,13 +325,23 @@ protected function hasPropertyValue(string $name): bool return isset($this->_properties[$name]); } - protected function requireProperties(array $names) + protected function requireProperties(array $names, array $atLeastOne = []) { foreach ($names as $name) { if (!isset($this->_properties[$name])) { $this->addError(" is missing required property: $name", get_class($this)); } } + + if (count($atLeastOne) > 0) { + foreach ($atLeastOne as $name) { + if (array_key_exists($name, $this->_properties)) { + return; + } + } + + $this->addError(" is missing at least one of the following required properties: " . implode(', ', $atLeastOne), get_class($this)); + } } protected function validateEmail(string $property) @@ -391,7 +401,7 @@ public function __unset($name) * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws exceptions\UnresolvableReferenceException in case resolving a reference fails. */ - public function resolveReferences(ReferenceContext $context = null) + public function resolveReferences(?ReferenceContext $context = null) { // avoid recursion to get stuck in a loop if ($this->_recursingReferences) { diff --git a/src/SpecObjectInterface.php b/src/SpecObjectInterface.php index a96aa74e..31f9f8cb 100644 --- a/src/SpecObjectInterface.php +++ b/src/SpecObjectInterface.php @@ -40,7 +40,7 @@ public function getErrors(): array; /** * Resolves all Reference Objects in this object and replaces them with their resolution. */ - public function resolveReferences(ReferenceContext $context = null); + public function resolveReferences(?ReferenceContext $context = null); /** * Set context for all Reference Objects in this object. diff --git a/src/json/JsonReference.php b/src/json/JsonReference.php index 5f248881..113cd1ec 100644 --- a/src/json/JsonReference.php +++ b/src/json/JsonReference.php @@ -28,6 +28,14 @@ final class JsonReference implements JsonSerializable * @var JsonPointer */ private $_pointer; + /** + * @var string|null + */ + private $_summary; + /** + * @var string|null + */ + private $_description; /** * Create a JSON Reference instance from a JSON document. @@ -42,7 +50,11 @@ public static function createFromJson(string $json): JsonReference if (!isset($refObject['$ref'])) { throw new MalformedJsonReferenceObjectException('JSON Reference Object must contain the "$ref" member.'); } - return static::createFromReference($refObject['$ref']); + return static::createFromReference( + $refObject['$ref'], + isset($refObject['summary']) ? $refObject['summary'] : null, + isset($refObject['description']) ? $refObject['description'] : null + ); } /** @@ -66,9 +78,14 @@ public static function createFromUri(string $uri, ?JsonPointer $jsonPointer = nu * @return JsonReference * @throws InvalidJsonPointerSyntaxException if an invalid JSON pointer string is passed as part of the fragment section. */ - public static function createFromReference(string $referenceURI): JsonReference - { + public static function createFromReference( + string $referenceURI, + ?string $summary = null, + ?string $description = null + ): JsonReference { $jsonReference = new JsonReference(); + $jsonReference->_summary = $summary; + $jsonReference->_description = $description; if (strpos($referenceURI, '#') !== false) { list($uri, $fragment) = explode('#', $referenceURI, 2); $jsonReference->_uri = $uri; @@ -129,6 +146,10 @@ public function getReference(): string #[\ReturnTypeWillChange] public function jsonSerialize() //: mixed { - return (object)['$ref' => $this->getReference()]; + return (object) array_filter([ + '$ref' => $this->getReference(), + 'summary' => $this->_summary, + 'description' => $this->_description, + ]); } } diff --git a/src/spec/Callback.php b/src/spec/Callback.php index 06bbd58a..dc2c4143 100644 --- a/src/spec/Callback.php +++ b/src/spec/Callback.php @@ -133,7 +133,7 @@ public function getErrors(): array * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws UnresolvableReferenceException */ - public function resolveReferences(ReferenceContext $context = null) + public function resolveReferences(?ReferenceContext $context = null) { if ($this->_pathItem !== null) { $this->_pathItem->resolveReferences($context); diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index 29d38b38..8843cc28 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -20,6 +20,7 @@ * @property Server[] $servers * @property Paths|PathItem[] $paths * @property Components|null $components + * @property PathItem[]|null $webhooks * @property SecurityRequirement[] $security * @property Tag[] $tags * @property ExternalDocumentation|null $externalDocs @@ -27,6 +28,15 @@ */ class OpenApi extends SpecBaseObject { + const VERSION_3_0 = '3.0'; + const VERSION_3_1 = '3.1'; + const VERSION_UNSUPPORTED = 'unsupported'; + + /** + * Pattern used to validate OpenAPI versions. + */ + const PATTERN_VERSION = '/^(3\.(0|1))\.\d+(-rc\d)?$/i'; + /** * @return array array of attributes available in this object. */ @@ -37,6 +47,7 @@ protected function attributes(): array 'info' => Info::class, 'servers' => [Server::class], 'paths' => Paths::class, + 'webhooks' => [PathItem::class], 'components' => Components::class, 'security' => [SecurityRequirement::class], 'tags' => [Tag::class], @@ -74,9 +85,40 @@ public function __get($name) */ public function performValidation() { - $this->requireProperties(['openapi', 'info', 'paths']); - if (!empty($this->openapi) && !preg_match('/^3\.0\.\d+(-rc\d)?$/i', $this->openapi)) { + if ($this->getMajorVersion() === static::VERSION_3_0) { + $this->requireProperties(['openapi', 'info', 'paths']); + } else { + $this->requireProperties(['openapi', 'info'], ['paths', 'webhooks', 'components']); + } + + if (!empty($this->openapi) && !preg_match(static::PATTERN_VERSION, $this->openapi)) { $this->addError('Unsupported openapi version: ' . $this->openapi); } } + + /** + * Returns the OpenAPI major version of the loaded OpenAPI description. + * @return string This returns a value of one of the `VERSION_*`-constants. Currently supported versions are: + * + * - `VERSION_3_0 = '3.0'` + * - `VERSION_3_1 = '3.1'` + * + * For unsupported version, this function will return `VERSION_UNSUPPORTED = 'unsupported'` + */ + public function getMajorVersion() + { + if (empty($this->openapi)) { + return self::VERSION_UNSUPPORTED; + } + if (preg_match(static::PATTERN_VERSION, $this->openapi, $matches)) { + switch ($matches[1]) { + case '3.0': + return static::VERSION_3_0; + case '3.1': + return static::VERSION_3_1; + } + } + + return self::VERSION_UNSUPPORTED; + } } diff --git a/src/spec/PathItem.php b/src/spec/PathItem.php index 4b2debcd..22e5b134 100644 --- a/src/spec/PathItem.php +++ b/src/spec/PathItem.php @@ -150,7 +150,7 @@ public function setReferenceContext(ReferenceContext $context) * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws \cebe\openapi\exceptions\UnresolvableReferenceException in case resolving a reference fails. */ - public function resolveReferences(ReferenceContext $context = null) + public function resolveReferences(?ReferenceContext $context = null) { if ($this->_ref instanceof Reference) { $pathItem = $this->_ref->resolve($context); diff --git a/src/spec/Paths.php b/src/spec/Paths.php index e504a90a..f55bafe1 100644 --- a/src/spec/Paths.php +++ b/src/spec/Paths.php @@ -246,7 +246,7 @@ public function getIterator(): Traversable * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws UnresolvableReferenceException */ - public function resolveReferences(ReferenceContext $context = null) + public function resolveReferences(?ReferenceContext $context = null) { foreach ($this->_paths as $key => $path) { if ($path === null) { diff --git a/src/spec/Reference.php b/src/spec/Reference.php index cda612a9..e784af75 100644 --- a/src/spec/Reference.php +++ b/src/spec/Reference.php @@ -8,7 +8,6 @@ namespace cebe\openapi\spec; use cebe\openapi\DocumentContextInterface; -use cebe\openapi\exceptions\IOException; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\exceptions\UnresolvableReferenceException; use cebe\openapi\json\InvalidJsonPointerSyntaxException; @@ -17,7 +16,6 @@ use cebe\openapi\json\NonexistentJsonPointerReferenceException; use cebe\openapi\ReferenceContext; use cebe\openapi\SpecObjectInterface; -use Symfony\Component\Yaml\Yaml; /** * Reference Object @@ -37,6 +35,14 @@ class Reference implements SpecObjectInterface, DocumentContextInterface * @var string */ private $_ref; + /** + * @var string|null + */ + private $_summary; + /** + * @var string|null + */ + private $_description; /** * @var JsonReference|null */ @@ -64,7 +70,7 @@ class Reference implements SpecObjectInterface, DocumentContextInterface * @param string $to class name of the type referenced by this Reference * @throws TypeErrorException in case invalid data is supplied. */ - public function __construct(array $data, string $to = null) + public function __construct(array $data, ?string $to = null) { if (!isset($data['$ref'])) { throw new TypeErrorException( @@ -81,15 +87,33 @@ public function __construct(array $data, string $to = null) 'Unable to instantiate Reference Object, value of $ref must be a string.' ); } + if (isset($data['summary']) && !is_string($data['summary'])) { + throw new TypeErrorException( + 'Unable to instantiate Reference Object, value of summary must be a string.' + ); + } + if (isset($data['description']) && !is_string($data['description'])) { + throw new TypeErrorException( + 'Unable to instantiate Reference Object, value of description must be a string.' + ); + } + $this->_to = $to; $this->_ref = $data['$ref']; + $this->_summary = $data['summary'] ?? null; + $this->_description = $data['description'] ?? null; try { - $this->_jsonReference = JsonReference::createFromReference($this->_ref); + $this->_jsonReference = JsonReference::createFromReference( + $this->_ref, + $this->_summary, + $this->_description + ); } catch (InvalidJsonPointerSyntaxException $e) { $this->_errors[] = 'Reference: value of $ref is not a valid JSON pointer: ' . $e->getMessage(); } - if (count($data) !== 1) { - $this->_errors[] = 'Reference: additional properties are given. Only $ref should be set in a Reference Object.'; + + if (!empty(array_diff(array_keys($data), ['$ref', 'summary', 'description']))) { + $this->_errors[] = 'Reference: additional properties are given. Only $ref, summary and description should be set in a Reference Object.'; } } @@ -99,7 +123,11 @@ public function __construct(array $data, string $to = null) */ public function getSerializableData() { - return (object) ['$ref' => $this->_ref]; + return (object) array_filter([ + '$ref' => $this->_ref, + 'summary' => $this->_summary, + 'description' => $this->_description, + ]); } /** @@ -135,6 +163,22 @@ public function getReference() return $this->_ref; } + /** + * @return string|null + */ + public function getSummary() + { + return $this->_summary; + } + + /** + * @return string|null + */ + public function getDescription() + { + return $this->_description; + } + /** * @return JsonReference the JSON Reference. */ @@ -170,7 +214,7 @@ public function getContext() : ?ReferenceContext * If you call resolveReferences() make sure to replace the Reference with the resolved object first. * @throws UnresolvableReferenceException in case of errors. */ - public function resolve(ReferenceContext $context = null) + public function resolve(?ReferenceContext $context = null) { if ($context === null) { $context = $this->getContext(); @@ -357,7 +401,7 @@ private function makeRelativePath($base, $path) * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws UnresolvableReferenceException */ - public function resolveReferences(ReferenceContext $context = null) + public function resolveReferences(?ReferenceContext $context = null) { throw new UnresolvableReferenceException('Cyclic reference detected, resolveReferences() called on a Reference Object.'); } diff --git a/src/spec/Responses.php b/src/spec/Responses.php index a6db447d..9e1233d2 100644 --- a/src/spec/Responses.php +++ b/src/spec/Responses.php @@ -236,7 +236,7 @@ public function getIterator(): Traversable * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws UnresolvableReferenceException */ - public function resolveReferences(ReferenceContext $context = null) + public function resolveReferences(?ReferenceContext $context = null) { foreach ($this->_responses as $key => $response) { if ($response instanceof Reference) { diff --git a/src/spec/Schema.php b/src/spec/Schema.php index 6c173795..49921d3c 100644 --- a/src/spec/Schema.php +++ b/src/spec/Schema.php @@ -26,9 +26,9 @@ * @property string $title * @property int|float $multipleOf * @property int|float $maximum - * @property bool $exclusiveMaximum + * @property bool|int|float $exclusiveMaximum * @property int|float $minimum - * @property bool $exclusiveMinimum + * @property bool|int|float $exclusiveMinimum * @property int $maxLength * @property int $minLength * @property string $pattern (This string SHOULD be a valid regular expression, according to the [ECMA 262 regular expression dialect](https://www.ecma-international.org/ecma-262/5.1/#sec-7.8.5)) @@ -40,7 +40,7 @@ * @property string[] $required list of required properties * @property array $enum * - * @property string $type + * @property string|string[] $type type can only be `string` in OpenAPI 3.0, but can be an array of strings since OpenAPI 3.1 * @property Schema[]|Reference[] $allOf * @property Schema[]|Reference[] $oneOf * @property Schema[]|Reference[] $anyOf @@ -75,9 +75,9 @@ protected function attributes(): array 'title' => Type::STRING, 'multipleOf' => Type::NUMBER, 'maximum' => Type::NUMBER, - 'exclusiveMaximum' => Type::BOOLEAN, + // 'exclusiveMaximum' => 'boolean' for 3.0 or 'number' for 3.1, handled in constructor, 'minimum' => Type::NUMBER, - 'exclusiveMinimum' => Type::BOOLEAN, + // 'exclusiveMinimum' => 'boolean' for 3.0 or 'number' for 3.1, handled in constructor, 'maxLength' => Type::INTEGER, 'minLength' => Type::INTEGER, 'pattern' => Type::STRING, @@ -151,6 +151,15 @@ public function __construct(array $data) throw new TypeErrorException(sprintf('Schema::$additionalProperties MUST be either boolean or a Schema/Reference object, "%s" given', $givenType)); } } + + if (isset($data['exclusiveMaximum']) && !in_array(gettype($data['exclusiveMaximum']), ['boolean', 'double', 'integer'])) { + throw new TypeErrorException(sprintf('Schema::$exclusiveMinimum MUST be either boolean or a number, "%s" given', gettype($data['exclusiveMaximum']))); + } + + if (isset($data['exclusiveMinimum']) && !in_array(gettype($data['exclusiveMinimum']), ['boolean', 'double', 'integer'])) { + throw new TypeErrorException(sprintf('Schema::$exclusiveMinimum MUST be either boolean or a number, "%s" given', gettype($data['exclusiveMinimum']))); + } + parent::__construct($data); } diff --git a/src/spec/Type.php b/src/spec/Type.php index 98610000..b4c81231 100644 --- a/src/spec/Type.php +++ b/src/spec/Type.php @@ -21,6 +21,7 @@ class Type const BOOLEAN = 'boolean'; const OBJECT = 'object'; const ARRAY = 'array'; + const NULL = 'null'; // Since OpenAPI 3.1 /** * Indicate whether a type is a scalar type, i.e. not an array or object. @@ -38,6 +39,7 @@ public static function isScalar(string $type): bool self::NUMBER, self::STRING, self::BOOLEAN, + self::NULL, ]); } } diff --git a/tests/ReaderTest.php b/tests/ReaderTest.php index 589b7cbb..927ec9b6 100644 --- a/tests/ReaderTest.php +++ b/tests/ReaderTest.php @@ -94,7 +94,7 @@ private function assertApiContent(\cebe\openapi\spec\OpenApi $openapi) */ public function testSymfonyYamlBugHunt() { - $openApiFile = __DIR__ . '/../vendor/oai/openapi-specification/examples/v3.0/uspto.yaml'; + $openApiFile = __DIR__ . '/../vendor/oai/openapi-specification-3.0/examples/v3.0/uspto.yaml'; $openapi = \cebe\openapi\Reader::readFromYamlFile($openApiFile); $inlineYamlExample = $openapi->paths['/']->get->responses['200']->content['application/json']->example; diff --git a/tests/ReferenceContextTest.php b/tests/ReferenceContextTest.php index b06eb5fc..549aec1a 100644 --- a/tests/ReferenceContextTest.php +++ b/tests/ReferenceContextTest.php @@ -6,7 +6,7 @@ class ReferenceContextTest extends \PHPUnit\Framework\TestCase { - public function resolveUriProvider() + public static function resolveUriProvider() { $data = [ [ @@ -124,7 +124,7 @@ public function testResolveUri($baseUri, $referencedUri, $expected) $this->assertEquals($expected, $context->resolveRelativeUri($referencedUri)); } - public function normalizeUriProvider() + public static function normalizeUriProvider() { $data = [ [ diff --git a/tests/docker/Dockerfile b/tests/docker/Dockerfile index 4dce3b53..447061ba 100644 --- a/tests/docker/Dockerfile +++ b/tests/docker/Dockerfile @@ -22,7 +22,7 @@ RUN apt-get update && \ --no-install-recommends && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ - && pecl install xdebug-2.9.6 \ + && pecl install xdebug-3.1.5 \ && docker-php-ext-enable xdebug \ && docker-php-ext-install \ zip \ @@ -43,15 +43,6 @@ RUN curl -o /tmp/composer-setup.php https://getcomposer.org/installer \ && php /tmp/composer-setup.php --no-ansi --install-dir=/usr/local/bin --filename=composer \ && rm -f /tmp/composer-setup.* -# Enable Xdebug -ENV XDEBUGINI_PATH=/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini - -RUN echo "xdebug.idekey=PHP_STORM" >> $XDEBUGINI_PATH && \ - echo "xdebug.default_enable=1" >> $XDEBUGINI_PATH && \ - echo "xdebug.remote_enable=1" >> $XDEBUGINI_PATH && \ - echo "xdebug.remote_connect_back=1" >> $XDEBUGINI_PATH && \ - echo "xdebug.remote_log=xdebug_remote.log" >> $XDEBUGINI_PATH && \ - echo "xdebug.remote_port=9000" >> $XDEBUGINI_PATH && \ - echo "xdebug.remote_autostart=1" >> $XDEBUGINI_PATH +COPY ./tests/docker/rootfs / WORKDIR /app diff --git a/tests/docker/rootfs/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini b/tests/docker/rootfs/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini new file mode 100644 index 00000000..afd03c8d --- /dev/null +++ b/tests/docker/rootfs/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini @@ -0,0 +1,12 @@ +zend_extension=xdebug.so +xdebug.mode=debug +xdebug.start_with_request=yes +xdebug.discover_client_host=false +## You need this for configure it in your IDE +xdebug.idekey="OPENAPI" +## This is for xdebug php cli +xdebug.trigger_value="OPENAPI" +xdebug.client_port=9003 +xdebug.log_level=7 +## And here we set that we do it through docker +xdebug.client_host=host.docker.internal \ No newline at end of file diff --git a/tests/json/JsonPointerTest.php b/tests/json/JsonPointerTest.php index eddd0ed7..af54ffcb 100644 --- a/tests/json/JsonPointerTest.php +++ b/tests/json/JsonPointerTest.php @@ -5,7 +5,7 @@ class JsonPointerTest extends \PHPUnit\Framework\TestCase { - public function encodeDecodeData() + public static function encodeDecodeData() { return [ ['~0', '~'], @@ -41,7 +41,7 @@ public function testDecode($encoded, $decoded) /** * @link https://tools.ietf.org/html/rfc6901#section-5 */ - public function rfcJsonDocument() + public static function rfcJsonDocument() { return <<rfcJsonDocument())], + ["" , "#" , json_decode(self::rfcJsonDocument())], ["/foo" , "#/foo" , ["bar", "baz"]], ["/foo/0", "#/foo/0", "bar"], ["/" , "#/" , 0], @@ -80,14 +80,14 @@ public function rfcExamples() ["/m~0n" , "#/m~0n" , 8], ]; foreach ($return as $example) { - $example[3] = $this->rfcJsonDocument(); + $example[3] = self::rfcJsonDocument(); yield $example; } } - public function allExamples() + public static function allExamples() { - yield from $this->rfcExamples(); + yield from self::rfcExamples(); yield ["/a#b" , "#/a%23b" , 16, '{"a#b": 16}']; } @@ -117,11 +117,11 @@ public function testUriEncoding($jsonPointer, $uriJsonPointer, $expectedEvaluati */ public function testEvaluation($jsonPointer, $uriJsonPointer, $expectedEvaluation) { - $document = json_decode($this->rfcJsonDocument()); + $document = json_decode(self::rfcJsonDocument()); $pointer = new JsonPointer($jsonPointer); $this->assertEquals($expectedEvaluation, $pointer->evaluate($document)); - $document = json_decode($this->rfcJsonDocument()); + $document = json_decode(self::rfcJsonDocument()); $reference = JsonReference::createFromReference($uriJsonPointer); $this->assertEquals($expectedEvaluation, $reference->getJsonPointer()->evaluate($document)); } diff --git a/tests/resources/definitions/account.yml b/tests/resources/definitions/account.yml new file mode 100755 index 00000000..dd1c1d48 --- /dev/null +++ b/tests/resources/definitions/account.yml @@ -0,0 +1,617 @@ +openapi: "3.0.2" +info: + title: "Nexmo Account API" + version: "1.0.0" + description : >- + Enables users to manage their Nexmo Account by programmable means. More information is available here: . + contact: + name: Nexmo.com + url: "https://developer.nexmo.com" +servers: + - url: "https://api.nexmo.com" +paths: + /account/get-balance: + servers: + - url: "https://rest.nexmo.com" + get: + operationId: getAccountBalance + summary: Get Account Balance + description: Retrieve the current balance of your Nexmo account + parameters: + - $ref: '#/components/parameters/api_key_for_auth' + - $ref: '#/components/parameters/api_secret_for_auth' + tags: + - Balance + responses: + '200': + description: The current balance of your account + content: + application/json: + schema: + $ref: '#/components/schemas/accountBalance' + example: {"value":10.17127500,"autoReload":false} + application/xml: + schema: + $ref: '#/components/schemas/accountBalance' + example: >- + + + 10.17127500 + false + + '401': + description: Not Authorised. You must supply your `api_key` and `api_secret` as query parameters to this request + + /account/top-up: + servers: + - url: "https://rest.nexmo.com" + post: + operationId: topUpAccountBalance + summary: Top Up Account Balance + description: >- + You can top up your account using this API when you have enabled auto-reload in the + dashboard. The amount added by the top-up operation will be the same amount as was + added in the payment when auto-reload was enabled. + + Your account balance is checked every 5-10 minutes and if it falls below the threshold + and auto-reload is enabled, then it will be topped up automatically. Use this endpoint + if you need to top up at times when your credit may be exhausted more quickly than the + auto-reload may occur. + externalDocs: + url: "https://help.nexmo.com/hc/en-us/articles/205603248-How-do-I-set-up-automatic-payments-using-PayPal-or-credit-card-" + description: Read more about automatic payments on the Knowledgebase + parameters: + - $ref: '#/components/parameters/api_key_for_auth' + - $ref: '#/components/parameters/api_secret_for_auth' + tags: + - Balance + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + type: object + required: + - trx + properties: + trx: + type: string + description: The transaction reference of the transaction when balance was added and auto-reload was enabled on your account. + responses: + '200': + description: Success + content: + application/json: + schema: + type: object + properties: + response: + type: string + example: "success" + application/xml: + schema: + type: object + properties: + response: + type: string + example: success + '401': + description: Not Authorised. You must supply your `api_key` and `api_secret` as query parameters to this request + + /account/settings: + servers: + - url: "https://rest.nexmo.com" + post: + operationId: changeAccountSettings + summary: Change Account Settings + description: >- + Update the default callback URLs (where the webhooks are sent to) associated with your account: + * Callback URL for incoming SMS messages + * Callback URL for delivery receipts + + Note that the URLs you provide must be valid and active. Nexmo will check that they + return a 200 OK response before the setting is saved. + parameters: + - $ref: '#/components/parameters/api_key_for_auth' + - $ref: '#/components/parameters/api_secret_for_auth' + tags: + - Configuration + requestBody: + required: false + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + moCallBackUrl: + description: >- + The URL where Nexmo will send a webhook when an SMS is received + to a Nexmo number that does not have SMS handling configured. + + Send an empty string to unset this value. + type: string + format: url + example: https://example.com/webhooks/inbound-sms + drCallBackUrl: + description: >- + The URL where Nexmo will send a webhook when an delivery + receipt is received without a specific callback URL configured. + + Send an empty string to unset this value. + type: string + format: url + example: https://example.com/webhooks/delivery-receipt + responses: + '200': + description: OK. Settings were updated if supplied, the details of the current settings are included in the response. + content: + application/json: + schema: + $ref: '#/components/schemas/accountSettings' + example: >- + { + "mo-callback-url": "https:\/\/example.com\/webhooks\/inbound-sms", + "dr-callback-url": "https:\/\/example.com\/webhooks\/delivery-receipt", + "max-outbound-request": 30, + "max-inbound-request": 30, + "max-calls-per-second": 30 + } + application/xml: + schema: + $ref: '#/components/schemas/accountSettings' + example: >- + + + https://example.com/webhooks/inbound-sms + https://example.com/webhooks/delivery-receipt + 30 + 30 + 30 + + '401': + description: Not Authorised. You must supply your `api_key` and `api_secret` as query parameters to this request + + /accounts/{api_key}/secrets: + get: + summary: Retrieve API Secrets + operationId: retrieveAPISecrets + tags: + - Secret Management + security: + - basicAuth: [] + parameters: + - $ref: '#/components/parameters/APIKey' + responses: + '200': + description: The list of your current API secrets + content: + application/json: + schema: + properties: + _links: + $ref: '#/components/schemas/secretMgmtLinks' + _embedded: + type: object + description: The single `secrets` key returns an array of API secrets + properties: + secrets: + type: array + description: Array of API secrets + items: + $ref: '#/components/schemas/secretInfo' + example: >- + { + "_links": { + "self": { + "href": "/accounts/abcd1234/secrets" + } + }, + "_embedded": { + "secrets": [ + { + "_links": { + "self": { + "href": "/accounts/abcd1234/secrets/01234567-aaaa-bbbb-cccc-defdefdefdef" + } + }, + "id": "01234567-aaaa-bbbb-cccc-defdefdefdef", + "created_at": "2018-12-03T10:07:23Z" + } + ] + } + } + '401': + $ref: '#/components/responses/BadCredentialsError' + '404': + description: "Item not found" + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorAPIKeyNotFound' + post: + summary: Create API Secret + operationId: createAPISecret + tags: + - Secret Management + security: + - basicAuth: [] + parameters: + - $ref: '#/components/parameters/APIKey' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - secret + properties: + secret: + description: | + The new secret must follow these rules: + + * minimum 8 characters + * maximum 25 characters + * minimum 1 lower case character + * minimum 1 upper case character + * minimum 1 digit + + type: string + example: 'example-4PI-secret' + responses: + '201': + description: Secret created + content: + application/json: + schema: + $ref: '#/components/schemas/secretInfo' + example: >- + { + "_links": { + "self": { + "href": "/accounts/abcd1234/secrets/01234567-aaaa-bbbb-cccc-defdefdefdef" + } + }, + "id": "01234567-aaaa-bbbb-cccc-defdefdefdef", + "created_at": "2018-12-03T10:07:23Z" + } + '400': + description: Bad request. This usually indicates valid data but can also occur when a user has exceeded the allowed number of secrets on their account. + content: + application/json: + schema: + properties: + type: + type: string + description: URL for further information + example: https://developer.nexmo.com/api-errors/account/secret-management#validation + title: + type: string + description: Description of the error + example: Bad Request + detail: + type: string + description: More detail regarding this error, including the API key supplied + example: The request failed due to secret validation errors + instance: + type: string + description: Internal Trace ID + example: bf0ca0bf927b3b52e3cb03217e1a1ddf + invalid_parameters: + type: array + description: Array of the parameters that are considered invalid, and explanations of why + items: + type: object + properties: + name: + type: string + description: Field name + example: secret + reason: + type: string + description: Explanation of why parameter is considered invalid + example: Does not meet complexity requirements + '401': + $ref: '#/components/responses/BadCredentialsError' + '404': + description: "Item not found" + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorAPIKeyNotFound' + + /accounts/{api_key}/secrets/{secret_id}: + get: + summary: Retrieve one API Secret + operationId: retrieveAPISecret + tags: + - Secret Management + security: + - basicAuth: [] + parameters: + - $ref: '#/components/parameters/APIKey' + - $ref: '#/components/parameters/secretId' + responses: + '200': + description: API secret response + content: + application/json: + schema: + $ref: '#/components/schemas/secretInfo' + example: >- + { + "_links": { + "self": { + "href": "/accounts/abcd1234/secrets/01234567-aaaa-bbbb-cccc-defdefdefdef" + } + }, + "id": "01234567-aaaa-bbbb-cccc-defdefdefdef", + "created_at": "2018-12-03T10:07:23Z" + } + '401': + $ref: '#/components/responses/BadCredentialsError' + '404': + description: "Item not found" + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/ErrorAPIKeyNotFound' + - $ref: '#/components/schemas/ErrorSecretIDNotFound' + delete: + summary: Revoke an API Secret + operationId: revokeAPISecret + tags: + - Secret Management + security: + - basicAuth: [] + parameters: + - $ref: '#/components/parameters/APIKey' + - $ref: '#/components/parameters/secretId' + responses: + '204': + description: Revoked secret response (without body content) + '401': + $ref: '#/components/responses/BadCredentialsError' + '403': + description: Operation forbidden by permissions or because this is the only API secret and you are required to have at least one. + content: + application/json: + schema: + properties: + type: + type: string + description: URL for further information + example: https://developer.nexmo.com/api-errors/account/secret-management#delete-last-secret + title: + type: string + description: Description of the error + example: Secret Deletion Forbidden + detail: + type: string + description: More detail regarding this error + example: Can not delete the last secret. The account must always have at least 1 secret active at any time + instance: + type: string + description: Internal Trace ID + example: bf0ca0bf927b3b52e3cb03217e1a1ddf + '404': + description: "Item not found" + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/ErrorAPIKeyNotFound' + - $ref: '#/components/schemas/ErrorSecretIDNotFound' + +tags: + - name: Balance + description: This section shows how you can query the current balance of your account, and if you have auto-reload enabled how to trigger a top-up to your account without waiting for the next balance check. + - name: Configuration + description: Manage the settings on your account + - name: Secret Management + description: >- + Many of Nexmo's APIs are accessed using an API key and secret. It is recommended that you change or "rotate" your secrets from time to time for security purposes. This section provides the API interface for achieving this. + + Note: to work on secrets for your secondary accounts, you may authenticate with your primary credentials and supply the secondary API keys as URL parameters to these API endpoints. + +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + description: >- + Provide an `Authorization` header, with a value of "Basic" followed by the result of base64-encoding your Nexmo API key and secret separated by a colon. You can find your API key and secret on the [dashboard](https://dashboard.nexmo.com) and more information is available [on the developer portal](https://developer.nexmo.com/concepts/guides/authentication#header-based-api-key-and-secret-authentication). + + If your API key were aaa012 and your API secret were abc123456789 then your HTTP header would be: `Authorization: Basic YWFhMDEyOmFiYzEyMzQ1Njc4OQ==` + + + parameters: + api_key_for_auth: + name: api_key + description: Your Nexmo API key. You can find this in the [dashboard](https://dashboard.nexmo.com) + in: query + required: true + schema: + type: string + example: abcd1234 + api_secret_for_auth: + name: api_secret + description: Your Nexmo API secret. You can find this in the [dashboard](https://dashboard.nexmo.com) + in: query + required: true + schema: + type: string + example: ABCDEFGH01234abc + APIKey: + name: api_key + in: path + description: The API key to manage secrets for + example: abcd1234 + required: true + schema: + type: string + secretId: + name: secret_id + in: path + description: ID of the API Secret + example: 01234567-aaaa-bbbb-cccc-defdefdefdef + required: true + schema: + type: string + schemas: + accountBalance: + type: object + xml: + name: accountBalance + properties: + value: + type: number + description: The balance of the account, in EUR + autoReload: + type: boolean + enum: [true, false] + description: Whether the account has auto-reloading enabled + accountSettings: + type: object + properties: + mo-callback-url: + type: string + format: url + description: The current or updated inbound message URI + example: https://example.com/webhooks/inbound-sms + dr-callback-url: + type: string + format: url + description: The current or updated delivery receipt URI + example: https://example.com/webhooks/delivery-receipt + max-outbound-request: + type: integer + description: The maximum number of outbound messages per second. + example: 30 + max-inbound-request: + type: integer + description: The maximum number of inbound messages per second. + example: 30 + max-calls-per-second: + type: integer + description: The maximum number of API calls per second. + example: 30 + secretInfo: + type: object + properties: + _links: + $ref: '#/components/schemas/secretMgmtLinks' + id: + type: string + description: Secret ID + example: ad6dc56f-07b5-46e1-a527-85530e625800 + created_at: + type: string + description: Creation date/time for this secret + example: '2017-03-02T16:34:49Z' + secretMgmtLinks: + type: object + description: Links related to this resource + properties: + self: + type: object + description: This resource + properties: + href: + type: string + description: The URI for this resource + ErrorAPIKeyNotFound: + description: This API key was not recognised + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + description: URL for further information + # example: https://developer.nexmo.com/api-errors#invalid-id + example: https://developer.nexmo.com/api-errors#invalid-api-key + title: + type: string + description: Description of the error + example: Invalid API Key + detail: + type: string + description: More detail regarding this error, including the API key supplied + example: API key 'abc123' not found + instance: + type: string + description: Internal Trace ID + example: bf0ca0bf927b3b52e3cb03217e1a1ddf + + ErrorSecretIDNotFound: + description: This secret ID was not recognised + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + description: URL for further information + example: https://developer.nexmo.com/api-errors#invalid-id + title: + type: string + description: Description of the error + example: Invalid ID + detail: + type: string + description: More detail regarding this error, including the secret ID supplied + example: ID '07239aeb-d756-4c32-a1de-cf64f8b21827' could not be found + instance: + type: string + description: Internal Trace ID + example: bf0ca0bf927b3b52e3cb03217e1a1ddf + + responses: + BadCredentialsError: + description: Credentials are missing or invalid + content: + application/json: + schema: + properties: + type: + type: string + description: URL for further information + example: https://developer.nexmo.com/ + title: + type: string + description: Description of the error + example: Unauthorized + detail: + type: string + description: More detail regarding this error, including the expected value + example: "Invalid credentials format. Expected: \"Authorization: (Base64(UTF-8(apiKey:secret)))\"" + instance: + type: string + description: Internal Trace ID + example: bf0ca0bf927b3b52e3cb03217e1a1ddf + +x-errors: + validation: + description: The provided payload is invalid + resolution: | + Modify your request to provide a valid payload. + * Minimum 8 characters + * Maximum 25 characters + * Minimum 1 lower case character + * Minimum 1 upper case character + * Minimum 1 digit + link: + text: View API reference + url: /api/account/api-secret-management#createSecret + + delete-last-secret: + description: You can not delete your only API secret + resolution: Add another API secret before deleting this one diff --git a/tests/resources/definitions/application.v2.yml b/tests/resources/definitions/application.v2.yml new file mode 100755 index 00000000..1dd79bc5 --- /dev/null +++ b/tests/resources/definitions/application.v2.yml @@ -0,0 +1,699 @@ +--- +openapi: "3.0.0" +info: + version: 2.0.5 + title: "Application API" + description: | + Nexmo provides an Application API to allow management of your Nexmo Applications. + + This API is backwards compatible with version 1. Applications created using version 1 of the API can also be managed using version 2 (this version) of the API. + contact: + name: Nexmo + url: 'https://developer.nexmo.com/' + email: devrel@nexmo.com +servers: + - url: https://api.nexmo.com/v2/applications +security: + - basicAuth: [] +paths: + /: + post: + summary: Create an application + operationId: createApplication + requestBody: + required: true + content: + application/json: + schema: + required: + - name + properties: + name: + description: "Application Name" + example: "Demo Application" + type: "string" + keys: + type: object + properties: + public_key: + type: string + example: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCA + KOxjsU4pf/sMFi9N0jqcSLcjxu33G + d/vynKnlw9SENi+UZR44GdjGdmfm1 + tL1eA7IBh2HNnkYXnAwYzKJoa4eO3 + 0kYWekeIZawIwe/g9faFgkev+1xsO + OUNhPx2LhuLmgwWSRS4L5W851Xe3f + UQIDAQAB + -----END PUBLIC KEY----- + description: Public key + capabilities: + type: object + description: "Your application can use multiple products. This contains the configuration for each product. This replaces the application `type` from version 1 of the Application API." + x-nexmo-developer-collection-description-shown: true + properties: + voice: + type: object + description: "Voice application webhook config" + x-nexmo-developer-collection-description-shown: true + properties: + webhooks: + type: object + properties: + answer_url: + type: object + description: "The URL that Nexmo make a request to when a call is placed/received. Must return an NCCO" + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://example.com/webhooks/answer + http_method: + type: string + example: GET + enum: + - GET + - POST + fallback_answer_url: + type: object + description: | + If your `answer_url` is offline or returns a HTTP error code, Nexmo will make a request to a + `fallback_answer_url` if it is set. This URL must return an NCCO. + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://fallback.example.com/webhooks/answer + http_method: + type: string + example: GET + enum: + - GET + - POST + event_url: + type: object + description: "Nexmo will send call events (e.g. `ringing`, `answered`) to this URL" + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://example.com/webhooks/event + http_method: + type: string + example: POST + enum: + - GET + - POST + rtc: + type: object + description: "RTC / Client SDK application webhook config" + x-nexmo-developer-collection-description-shown: true + properties: + webhooks: + type: object + properties: + event_url: + type: object + description: "Nexmo will send RTC events to this URL" + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://example.com/webhooks/event + http_method: + type: string + example: POST + enum: + - GET + - POST + messages: + type: object + description: "Messages and Dispatch application webhook config" + x-nexmo-developer-collection-description-shown: true + properties: + webhooks: + type: object + properties: + inbound_url: + type: object + description: "Nexmo will forward inbound messages to this URL" + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://example.com/webhooks/inbound + http_method: + type: string + example: POST + enum: + - POST + status_url: + type: object + description: "Nexmo will send message status updates (e.g. `delivered`, `seen`) to this URL" + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://example.com/webhooks/status + http_method: + type: string + example: POST + enum: + - POST + vbc: + type: object + description: "Specify `vbc` capability to enable zero-rated calls for VBC number programmability service applications. This must be an empty object." + x-nexmo-developer-collection-description-shown: true + responses: + '201': + description: Success + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApplicationResponse' + - type: object + properties: + keys: + type: object + properties: + public_key: + type: string + example: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCA + KOxjsU4pf/sMFi9N0jqcSLcjxu33G + d/vynKnlw9SENi+UZR44GdjGdmfm1 + tL1eA7IBh2HNnkYXnAwYzKJoa4eO3 + 0kYWekeIZawIwe/g9faFgkev+1xsO + OUNhPx2LhuLmgwWSRS4L5W851Xe3f + UQIDAQAB + -----END PUBLIC KEY----- + private_key: + type: string + example: | + -----BEGIN PRIVATE KEY----- + MIIEvQIBADANBgkqhkiG9w0BAQEFA + ASCBKcwggSjAgEAAoIBAQDEPpvi+3 + RH1efQ\nkveWzZDrNNoEXmBw61w+O + 0u/N36tJnN5XnYecU64yHzu2ByEr0 + 7iIvYbavFnADwl\nHMTJwqDQakpa3 + 8/SFRnTDq3zronvNZ6nOp7S6K7pcZ + rw/CvrL6hXT1x7cGBZ4jPx\nqhjqY + uJPgZD7OVB69oYOV92vIIJ7JLYwqb + -----END PRIVATE KEY----- + '400': + $ref: '#/components/responses/InvalidPayloadError' + '401': + $ref: 'common/common_errors.yml#/components/responses/BadCredentialsError' + '405': + $ref: 'common/common_errors.yml#/components/responses/InvalidRequestMethod' + '406': + $ref: 'common/common_errors.yml#/components/responses/InvalidAcceptHeader' + '415': + $ref: 'common/common_errors.yml#/components/responses/UnsupportedContentTypeHeader' + + get: + operationId: listApplication + summary: List available applications + parameters: + - name: page_size + in: query + description: The number of applications per page + schema: + type: integer + - name: page + in: query + description: The current page number (starts at 1) + schema: + type: integer + + responses: + '200': + description: Success + content: + application/json: + schema: + properties: + page_size: + type: integer + example: 10 + description: The number of applications per page + page: + type: integer + example: 1 + description: The current page number (starts at 1) + total_items: + type: integer + example: 6 + description: The total number of applications + total_pages: + type: integer + example: 1 + description: The total number of pages returned + _embedded: + type: object + description: A list of applications matching your existing filters + properties: + applications: + type: array + items: + $ref: '#/components/schemas/ApplicationResponse' + '400': + description: Invalid Request + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors/application#list-validation' + title: + type: string + example: Bad Request + detail: + type: string + example: The request failed due to validation errors + invalid_parameters: + type: array + items: + type: object + properties: + name: + type: string + example: page_size + reason: + type: string + example: must be between 1 and 100 + instance: + $ref: 'common/common_errors.yml#/components/fields/instance' + '401': + $ref: 'common/common_errors.yml#/components/responses/BadCredentialsError' + '405': + $ref: 'common/common_errors.yml#/components/responses/InvalidRequestMethod' + '406': + $ref: 'common/common_errors.yml#/components/responses/InvalidAcceptHeader' + + /{id}: + parameters: + - name: id + in: path + required: true + description: The ID of the application + example: 78d335fa323d01149c3dd6f0d48968cf + schema: + type: string + get: + operationId: getApplication + summary: Get an application + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationResponse' + '401': + $ref: 'common/common_errors.yml#/components/responses/BadCredentialsError' + '404': + $ref: 'common/common_errors.yml#/components/responses/NotFoundError' + '405': + $ref: 'common/common_errors.yml#/components/responses/InvalidRequestMethod' + '406': + $ref: 'common/common_errors.yml#/components/responses/InvalidAcceptHeader' + + put: + summary: Update an application + operationId: updateApplication + requestBody: + required: true + content: + application/json: + schema: + required: + - name + properties: + name: + description: "Application Name" + example: "Demo Application" + type: "string" + keys: + type: object + properties: + public_key: + type: string + example: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCA + KOxjsU4pf/sMFi9N0jqcSLcjxu33G + d/vynKnlw9SENi+UZR44GdjGdmfm1 + tL1eA7IBh2HNnkYXnAwYzKJoa4eO3 + 0kYWekeIZawIwe/g9faFgkev+1xsO + OUNhPx2LhuLmgwWSRS4L5W851Xe3f + UQIDAQAB + -----END PUBLIC KEY----- + description: Public key + capabilities: + type: object + description: "Your application can use multiple products. This contains the configuration for each product. This replaces the application `type` from version 1 of the Application API." + x-nexmo-developer-collection-description-shown: true + properties: + voice: + type: object + description: "Voice application webhook config" + x-nexmo-developer-collection-description-shown: true + properties: + webhooks: + type: object + properties: + answer_url: + type: object + description: "The URL that Nexmo make a request to when a call is placed/received. Must return an NCCO" + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://example.com/webhooks/answer + http_method: + type: string + example: GET + enum: + - GET + - POST + fallback_answer_url: + type: object + description: | + If your `answer_url` is offline or returns a HTTP error code, Nexmo will make a request to a + `fallback_answer_url` if it is set. This URL must return an NCCO + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://fallback.example.com/webhooks/answer + http_method: + type: string + example: GET + enum: + - GET + - POST + + event_url: + type: object + description: "Nexmo will send call events (e.g. `ringing`, `answered`) to this URL" + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://example.com/webhooks/event + http_method: + type: string + example: POST + enum: + - GET + - POST + rtc: + type: object + description: "RTC / Client SDK application webhook config" + x-nexmo-developer-collection-description-shown: true + properties: + webhooks: + type: object + properties: + event_url: + type: object + description: "Nexmo will send RTC events to this URL" + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://example.com/webhooks/event + http_method: + type: string + example: POST + enum: + - GET + - POST + messages: + type: object + description: "Messages and Dispatch application webhook config" + x-nexmo-developer-collection-description-shown: true + properties: + webhooks: + type: object + properties: + inbound_url: + type: object + description: "Nexmo will forward inbound messages to this URL" + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://example.com/webhooks/inbound + http_method: + type: string + example: POST + enum: + - POST + status_url: + type: object + description: "Nexmo will send message status updates (e.g. `delivered`, `seen`) to this URL" + x-nexmo-developer-collection-description-shown: true + properties: + address: + type: string + example: https://example.com/webhooks/status + http_method: + type: string + example: POST + enum: + - POST + vbc: + type: object + description: "Specify the `vbc` capability to enable zero-rated calls for VBC number programmability service applications. This must be an empty object." + x-nexmo-developer-collection-description-shown: true + responses: + '200': + description: Success + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApplicationResponse' + - type: object + properties: + keys: + type: object + properties: + public_key: + type: string + example: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCA + KOxjsU4pf/sMFi9N0jqcSLcjxu33G + d/vynKnlw9SENi+UZR44GdjGdmfm1 + tL1eA7IBh2HNnkYXnAwYzKJoa4eO3 + 0kYWekeIZawIwe/g9faFgkev+1xsO + OUNhPx2LhuLmgwWSRS4L5W851Xe3f + UQIDAQAB + -----END PUBLIC KEY----- + private_key: + type: string + example: | + -----BEGIN PRIVATE KEY----- + MIIEvQIBADANBgkqhkiG9w0BAQEFA + ASCBKcwggSjAgEAAoIBAQDEPpvi+3 + RH1efQ\nkveWzZDrNNoEXmBw61w+O + 0u/N36tJnN5XnYecU64yHzu2ByEr0 + 7iIvYbavFnADwl\nHMTJwqDQakpa3 + 8/SFRnTDq3zronvNZ6nOp7S6K7pcZ + rw/CvrL6hXT1x7cGBZ4jPx\nqhjqY + uJPgZD7OVB69oYOV92vIIJ7JLYwqb + -----END PRIVATE KEY----- + '400': + $ref: '#/components/responses/InvalidPayloadError' + '401': + $ref: 'common/common_errors.yml#/components/responses/BadCredentialsError' + '404': + $ref: 'common/common_errors.yml#/components/responses/NotFoundError' + '405': + $ref: 'common/common_errors.yml#/components/responses/InvalidRequestMethod' + '406': + $ref: 'common/common_errors.yml#/components/responses/InvalidAcceptHeader' + '415': + $ref: 'common/common_errors.yml#/components/responses/UnsupportedContentTypeHeader' + delete: + operationId: deleteApplication + summary: Delete an application + description: Deleting an application **cannot be undone**. + responses: + '204': + description: Success + '401': + $ref: 'common/common_errors.yml#/components/responses/BadCredentialsError' + '404': + $ref: 'common/common_errors.yml#/components/responses/NotFoundError' + '405': + $ref: 'common/common_errors.yml#/components/responses/InvalidRequestMethod' + '406': + $ref: 'common/common_errors.yml#/components/responses/InvalidAcceptHeader' + +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + responses: + InvalidPayloadError: + description: Invalid Request + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors/application#payload-validation' + title: + type: string + example: Bad Request + detail: + type: string + example: The request failed due to validation errors + invalid_parameters: + type: array + items: + type: object + properties: + name: + type: string + example: capabilities.voice.webhooks.answer_url.http_method + reason: + type: string + example: 'must be one of: GET, POST' + instance: + $ref: 'common/common_errors.yml#/components/fields/instance' + + schemas: + ApplicationResponse: + properties: + id: + type: string + example: 78d335fa323d01149c3dd6f0d48968cf + description: The application's ID + name: + type: string + example: My Application + description: Friendly identifier for your application. This is not unique + capabilities: + type: object + description: Configuration for the products available in this application + properties: + voice: + type: object + description: Voice related configuration + properties: + webhooks: + type: object + properties: + answer_url: + type: object + properties: + address: + type: string + example: https://example.com/webhooks/answer + description: The URL that Nexmo requests when a call is placed/received. Must return an NCCO + http_method: + type: string + example: POST + description: The HTTP method used to fetch your NCCO from your `answer_url` + fallback_answer_url: + type: object + properties: + address: + type: string + example: https://fallback.example.com/webhooks/answer + description: | + If your `answer_url` is offline or returns a HTTP error code, Nexmo will make a request to a + `fallback_answer_url` if it is set. This URL must return an NCCO. + http_method: + type: string + example: POST + description: The HTTP method used to fetch your NCCO from your `answer_url` + event_url: + type: object + properties: + address: + type: string + example: https://example.com/webhooks/event + description: The URL that Nexmo sends events related to your call to + http_method: + type: string + example: POST + description: The HTTP method used to send events to your server + messages: + type: object + description: Messages / Dispatch related configuration + properties: + webhooks: + type: object + properties: + inbound_url: + type: object + properties: + address: + type: string + example: https://example.com/webhooks/inbound + description: The URL that Nexmo forwards inbound messages to on your server + http_method: + type: string + example: POST + description: The HTTP method used to send inbound messages to your server + status_url: + type: object + properties: + address: + type: string + example: https://example.com/webhooks/status + description: The URL that Nexmo sends events related to your messages to + http_method: + type: string + example: POST + description: The HTTP method used to send events to your server (always `POST`) + rtc: + type: object + description: RTC / Conversation Service related configuration + properties: + webhooks: + type: object + properties: + event_url: + type: object + properties: + address: + type: string + example: https://example.com/webhooks/event + http_method: + type: string + example: POST + vbc: + type: object + description: "Specify the `vbc` capability to enable zero-rated calls for VBC number programmability service applications. This is always an empty object." + +x-errors: + payload-validation: + description: Invalid request. See `invalid_parameters` field for details + resolution: Review the documentation and send a valid `POST` request. + link: + text: View API reference + url: /api/application.v2#createApplication + + list-validation: + description: Invalid request. See `invalid_parameters` field for details + resolution: Review the documentation and send a valid `GET` request. + link: + text: View API reference + url: /api/application.v2#listApplication + + rate-limit: + description: The request was rate limited + resolution: The Redact API supports 170 requests per second. Reduce the frequency of your requests. diff --git a/tests/resources/definitions/application.yml b/tests/resources/definitions/application.yml new file mode 100755 index 00000000..3e5b4e54 --- /dev/null +++ b/tests/resources/definitions/application.yml @@ -0,0 +1,482 @@ +openapi: 3.0.0 +info: + title: Nexmo Application API + version: 1.0.2 + description: >- +
+ +
+

Applications V1 is deprecated

+ This version of the API has been deprecated. Please use version 2 going forwards +
+
+ + A Nexmo application contains the security and configuration information you + need to connect to Nexmo endpoints and easily use our products. + contact: + name: Nexmo.com + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' + x-twitter: Nexmo + termsOfService: 'https://www.nexmo.com/terms-of-use' + license: + name: The MIT License (MIT) + url: 'https://opensource.org/licenses/MIT' + x-logo: + url: 'https://twitter.com/Nexmo/profile_image?size=original' + x-apiClientRegistration: 'https://dashboard.nexmo.com/sign-up' +servers: + - url: 'https://api.nexmo.com/v1/applications' +externalDocs: + url: 'https://developer.nexmo.com/api/developer/application' + x-sha1: d8836c374e2a7504bd2cd59e05fcee440f67cb44 +paths: + /: + post: + summary: Create Application + description: You use a `POST` request to create a new application. + operationId: createApplication + requestBody: + content: + application/json: + schema: + type: object + required: + - api_key + - api_secret + - name + - type + properties: + api_key: + $ref: '#/components/schemas/apiKey' + api_secret: + $ref: '#/components/schemas/apiSecret' + name: + description: The name of your application. + type: string + example: My Application + type: + description: >- + The Nexmo product or products that you access with this + application. Currently `voice` and `messages` application types are supported. + type: string + example: voice + enum: + - voice + - messages + answer_url: + description: >- + The URL where your webhook delivers the Nexmo Call Control + Object that governs this call. As soon as your user answers + a call Nexmo makes a request to `answer_url`. Required for inbound calls only. + type: string + example: 'https://example.com/webhooks/answer' + answer_method: + description: >- + The HTTP method used to make the request to `answer_url`. + The default value is `GET`. + type: string + example: GET + event_url: + description: >- + Nexmo sends event information asynchronously to this URL + when status changes for `voice` applications. Always required for `voice` applications. + type: string + example: 'https://example.com/webhooks/event' + event_method: + description: >- + The HTTP method used to send event information to `event_url`. + The default value is `POST`. For `voice` type applications only. + type: string + example: POST + status_url: + description: >- + Nexmo sends event information asynchronously to this URL + when status changes. Required for `messages` type applications only. + type: string + example: 'https://example.com/webhooks/status' + status_method: + description: >- + The HTTP method used to send event information to `status_url`. + The default value is `POST`. For `messages` type applications only. + type: string + example: POST + inbound_url: + description: >- + Nexmo sends a request to this URL when an inbound message + is received. Required for `messages` type applications only. + type: string + example: 'https://example.com/webhooks/inbound' + inbound_method: + description: >- + The HTTP method used to send event information to `inbound_url`. + The default value is `POST`. For `messages` type applications only. + type: string + example: POST + responses: + '201': + description: Created + content: + application/json: + schema: + $ref: '#/components/schemas/applicationCreated' + get: + summary: Retrieve all Applications + description: >- + You use a `GET` request to retrieve details of all applications + associated with your account. + operationId: retrieveApplications + parameters: + - $ref: '#/components/parameters/apiKeyQueryString' + - $ref: '#/components/parameters/apiSecretQueryString' + - name: page_size + required: false + in: query + description: >- + Set the number of items returned on each call to this endpoint. The + default is 10 records. + schema: + type: integer + default: 10 + example: 10 + - name: page_index + required: false + in: query + description: Set the offset from the first page. The default value is `0`. + schema: + type: integer + default: 0 + example: 0 + + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/applications' + '/{app_id}': + get: + summary: Retrieve Application + description: You use a `GET` request to retrieve details about a single application. + operationId: retrieveApplication + parameters: + - $ref: '#/components/parameters/apiKeyQueryString' + - $ref: '#/components/parameters/apiSecretQueryString' + - $ref: '#/components/parameters/app_id' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/application' + put: + summary: Update Application + description: You use a `PUT` request to update an existing application. + operationId: updateApplication + parameters: + - $ref: '#/components/parameters/app_id' + requestBody: + content: + application/json: + schema: + type: object + required: + - api_key + - api_secret + - name + - type + properties: + api_key: + $ref: '#/components/schemas/apiKey' + api_secret: + $ref: '#/components/schemas/apiSecret' + name: + description: The name of your application. + type: string + example: UpdatedApplication + type: + description: >- + The Nexmo product or products that you access with this application. + Currently `voice` and `messages` application types are supported. You + can't change the type of application. + type: string + enum: + - voice + - messages + example: voice + answer_url: + description: >- + The URL where your webhook delivers the Nexmo Call Control Object + that governs this call. As soon as your user answers a call Nexmo + makes a request to `answer_url`. + type: string + format: url + example: 'https://example.com/webhooks/answer' + answer_method: + description: >- + The HTTP method used to make the request to `answer_url`. The + default value is `GET`. + type: string + default: GET + example: GET + event_url: + description: >- + Nexmo sends event information asynchronously to this URL when status + changes. + type: string + format: url + example: 'https://example.com/webhooks/event' + event_method: + description: >- + The HTTP method used to send event information to `event_url`. The + default value is POST. + type: string + default: POST + example: POST + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/application' + delete: + summary: Destroy Application + description: You use a `DELETE` request to delete a single application. + operationId: deleteApplication + parameters: + - $ref: '#/components/parameters/app_id' + responses: + '204': + description: No content +components: + parameters: + app_id: + name: app_id + in: path + required: true + description: The ID allocated to your application by Nexmo. + schema: + type: string + example: aaaaaaaa-bbbb-cccc-dddd-0123456789ab + apiKeyQueryString: + schema: + type: string + name: api_key + in: query + required: true + description: >- + You can find your API key in your [account + overview](https://dashboard.nexmo.com/account-overview) + apiSecretQueryString: + schema: + type: string + name: api_secret + in: query + required: true + description: >- + You can find your API secret in your [account + overview](https://dashboard.nexmo.com/account-overview) + + schemas: + apiKey: + type: string + description: >- + You can find your API key in your [account + overview](https://dashboard.nexmo.com/account-overview) + apiSecret: + type: string + description: >- + You can find your API secret in your [account + overview](https://dashboard.nexmo.com/account-overview) + links: + type: object + description: >- + A series of links between resources in this API in the following [HAL + specification](http://stateless.co/hal_specification.html). + properties: + href: + type: string + format: url + description: The link URL. + example: /v1/applications/aaaaaaaa-bbbb-cccc-dddd-0123456789ab + voice: + description: >- + The Nexmo product that you access with this application. + type: object + properties: + webhooks: + type: array + items: + type: object + required: + - endpoint_type + - endpoint + - http_method + properties: + endpoint_type: + type: string + enum: + - answer_url + - event_url + example: answer_url + endpoint: + type: string + description: >- + `answer_url`: The URL where your webhook delivers the Nexmo + Call Control Object that governs this call. As soon as your + user answers a call Nexmo makes a request to answer_url. + `event_url`: Nexmo sends event information asynchronously to + this URL when status changes. + format: url + example: 'https://example.com/webhooks/answer' + http_method: + type: string + description: >- + The HTTP method used to send event information to the + `event_url` or `answer_url`. + enum: + - GET + - POST + example: GET + messages: + description: >- + The Nexmo product that you access with this application. + type: object + properties: + webhooks: + type: array + items: + type: object + required: + - endpoint_type + - endpoint + - http_method + properties: + endpoint_type: + type: string + enum: + - inbound_url + - status_url + example: status_url + endpoint: + type: string + description: >- + `inbound_url`: The URL where inbound messages are delivered. + `status_url`: The URL where message status is delivered. + format: url + example: 'https://example.com/webhooks/status' + http_method: + type: string + description: >- + The HTTP method used to send data to the + `inbound_url` or `status_url`. Default is POST. + enum: + - GET + - POST + example: POST + keys: + description: >- + The Nexmo product that you access with this application. + type: object + properties: + public_key: + description: >- + The public key used to validate the + [JWT](https://en.wikipedia.org/wiki/JSON_Web_Token). + type: string + example: PUBLIC_KEY + keysWithPrivateKey: + allOf: + - $ref: '#/components/schemas/keys' + - properties: + private_key: + description: >- + The private key you use to generate the JSON Web Token (JWT) + that authenticates your requests to Messages API. + type: string + example: PRIVATE_KEY + applicationCreated: + allOf: + - $ref: '#/components/schemas/application' + - properties: + keys: + $ref: '#/components/schemas/keysWithPrivateKey' + application: + type: object + required: + - name + - voice + - messages + properties: + id: + $ref: '#/components/schemas/applicationBase/properties/id' + name: + $ref: '#/components/schemas/applicationBase/properties/name' + voice: + $ref: '#/components/schemas/voice' + messages: + $ref: '#/components/schemas/messages' + keys: + $ref: '#/components/schemas/keys' + _links: + $ref: '#/components/schemas/links' + applicationBase: + type: object + properties: + id: + description: The ID allocated to your application by Nexmo. + type: string + example: aaaaaaaa-bbbb-cccc-dddd-0123456789ab + name: + description: The name of your application + type: string + example: My Application + applications: + type: object + required: + - count + - page_size + - page_index + - _embedded + - _links + properties: + count: + $ref: '#/components/schemas/applicationsBase/properties/count' + page_size: + $ref: '#/components/schemas/applicationsBase/properties/page_size' + page_index: + $ref: '#/components/schemas/applicationsBase/properties/page_index' + _embedded: + $ref: '#/components/schemas/applicationsBase/properties/_embedded' + _links: + $ref: '#/components/schemas/links' + applicationsBase: + properties: + count: + description: The number of items associated with your account. + type: integer + example: 1 + page_size: + description: >- + The number of items returned on each call to this endpoint. The + default is 10 records. + type: integer + example: 10 + page_index: + description: The offset from the first page. + type: integer + example: 1 + _embedded: + type: object + properties: + applications: + type: array + description: >- + The collection of your applications. Each object contains + information about an an individual application. The public_key + is not included in the application information. + items: + $ref: '#/components/schemas/application' diff --git a/tests/resources/definitions/audit.yml b/tests/resources/definitions/audit.yml new file mode 100755 index 00000000..9833645b --- /dev/null +++ b/tests/resources/definitions/audit.yml @@ -0,0 +1,563 @@ +openapi: "3.0.0" + +info: + version: 1.0.2 + title: Nexmo Audit API + description: > + Nexmo's Audit API allows you to view details of changes to your account. More information is available at . + + _Please note that the Audit API is currently in Beta_ + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' + termsOfService: 'https://www.nexmo.com/terms-of-use' + license: + name: The MIT License (MIT) + url: 'https://opensource.org/licenses/MIT' + +servers: + - url: https://api.nexmo.com/beta/audit + +paths: + '/events': + get: + operationId: getEvents + summary: Retrieve audit events + description: > + Get a series of audit events describing changes made to your + Nexmo account over time. + parameters: + - name: event_type + description: Filter results by this event type. + in: query + schema: + $ref: '#/components/schemas/EventTypes' + - name: date_from + schema: + type: string + description: Start of time range for audit events. DateTime in ISO-8601 format. + in: query + - name: date_to + schema: + type: string + description: End of time range for audit events. DateTime in ISO-8601 format. + in: query + - name: search_text + schema: + type: string + description: Return only audit events where the JSON object contains this search text. Must be legal text for a JSON attribute value, that is quotes and braces must be escaped. + in: query + - name: page + schema: + type: string + description: Page number starting with page 1. + in: query + - name: size + schema: + type: integer + minimum: 1 + maximum: 100 + default: 30 + description: Number of elements per page. + in: query + security: + - basicAuth: [] + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/AuditResp' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorUnauthorized' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorForbidden' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorNotFound' + + + options: + operationId: getEventsOptions + summary: Retrieve audit event types + description: > + Get audit event types. + security: + - basicAuth: [] + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/AuditEventTypesResp' + + '204': + description: No Content + content: + application/json: + schema: + $ref: '#/components/schemas/NoContent' + + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorUnauthorized' + + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorForbidden' + + + # GET event + '/events/{id}': + get: + operationId: getEvent + summary: Retrieve individual audit event + description: > + Get the specified audit event. + parameters: + - name: id + schema: + type: string + description: The UUID of the audit event to retrieve + in: path + required: true + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + security: + - basicAuth: [] + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/AuditEvent' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorUnauthorized' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorNotFound' + +components: + schemas: + EventTypes: + type: string + description: "The type of the audit event." + example: "APP_CREATE" + enum: + - USER_STATUS + - USER_UPDATE + - USER_BILLING_UPDATE + - USER_CREATE + - USER_LOGIN + - USER_LOGOUT + - USER_PRODUCT_SEARCH + - USER_API_KEYS_UPDATE + - ACCOUNT_SECRET_DELETE + - ACCOUNT_SECRET_CREATE + - ACCOUNT_UPDATE_SPAMMER + - ACCOUNT_UPDATE_SETTINGS_API + - NUMBER_ASSIGN + - NUMBER_UPDATED + - NUMBER_RELEASE + - NUMBER_LINKED + - NUMBER_UNLINKED + - APP_CREATE + - APP_UPDATE + - APP_DELETE + - APP_DISABLE + - APP_ENABLE + - IP_WHITELIST_CREATE + - IP_WHITELIST_DELETE + - AUTORELOAD_ENABLE + - AUTORELOAD_UPDATE + - AUTORELOAD_DISABLE + + AuditResp: + type: object + properties: + _embedded: + type: object + description: Container containing the `events` array. + properties: + events: + type: array + items: + $ref: '#/components/schemas/AuditEvent' + _links: + $ref: '#/components/schemas/PaginationLinks' + page: + $ref: '#/components/schemas/PaginationData' + + AuditEvent: + type: object + properties: + id: + type: string + description: "UUID of the audit event" + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + event_type: + $ref: '#/components/schemas/EventTypes' + event_type_description: + type: string + description: "A description of the event type" + example: "Application created." + created_at: + type: string + description: "When the audit event was created." + example: "2018-07-04T11:41:32" + format: date + user_email: + type: string + description: "Email of the user whose account the audit event is associated with." + format: email + example: user@example.org + user_id: + type: integer + description: "The ID of the user that the audit event is associated with." + example: 1234567 + account_id: + type: string + description: "The NEXMO_API_KEY of the Nexmo account that the audit event is associated with." + example: "abcd1234" + source: + type: string + description: > + The source of the event. + + CD: Customer Dashboard + DEVAPI: via Developer API + enum: + - CD + - DEVAPI + example: CD + source_ip: + type: string + format: ipv4 + description: The IP address used to make the account change. + example: "192.0.2.0" + source_description: + type: string + description: "Description of the audit event source." + format: A human readable description of the `source`. + enum: + - Customer Dashboard + - Developer API + example: Customer Dashboard + source_country: + type: string + description: ISO 3166-1 Alpha-2 country code recorded for the event. + example: GB + context: + type: object + description: A valid JSON object with information detailing the context of the audit event. + oneOf: + - $ref: '#/components/schemas/ContextAppCreate' + - $ref: '#/components/schemas/ContextNumberLinking' + - $ref: '#/components/schemas/ContextNumberUpdate' + # TODO: there's a lot to add here! + example: + created: + accountId: "abcdef01" + appId: aaaaaaaa-bbbb-cccc-dddd-0123456789ab + name: My voice app + answer_url: + method: GET + url: https://example.org/call + type: voice + event_url: + method: POST + url: https://example.org/event + _links: + $ref: '#/components/schemas/EventLink' + + AuditEventTypesResp: + type: object + properties: + eventTypes: + type: array + description: An array of audit event types. + items: + $ref: '#/components/schemas/AuditEventType' + + AuditEventType: + type: object + properties: + type: + $ref: '#/components/schemas/EventTypes' + description: + type: string + example: "Application created." + description: "A description of the event type" + + EventLink: + type: object + description: A link to this audit event object if you were to retrieve it individually. + properties: + self: + properties: + href: + type: string + format: uri + description: URI of this document. + example: "https://api.nexmo.com/beta/audit/events/aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + + PaginationLinks: + type: object + description: Container containing self, next and last page links. + properties: + self: + type: object + properties: + href: + type: string + format: uri + description: URI of this document. + example: "http://api.nexmo.com/beta/audit/events" + next: + type: object + properties: + href: + type: string + format: uri + description: URI of the next document. + example: "http://api.nexmo.com/beta/audit/events?page=2&size=30" + last: + type: object + properties: + href: + type: string + format: uri + description: URI of the last document in the sequence. + example: "http://api.nexmo.com/beta/audit/events?page=2&size=30" + + PaginationData: + type: object + description: Page containing results. + properties: + size: + type: integer + minimum: 1 + maximum: 100 + default: 30 + example: 100 + description: The total number of items in this response (page). + totalElements: + type: integer + example: 120 + description: The total number of items across all pages. + totalPages: + type: integer + example: 2 + description: The number of pages of data in the response. + number: + type: integer + example: 1 + description: Which page of the pagination sequence you are looking at. + + CallbackUrl: + type: object + properties: + method: + type: string + example: GET + enum: + - GET + - POST + description: "The HTTP method used to make a callback to your application" + url: + type: string + example: "https://example.org/my-app-callback" + format: url + description: "The URL used for a callback to your application" + + ContextAppCreate: + type: object + properties: + appId: + type: string + description: "UUID of the app that was created" + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + created: + type: object + properties: + accountId: + type: string + example: "abcdef01" + description: "Which account the app is associated with" + appId: + type: string + description: "UUID of the app that was created" + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + name: + type: string + description: "The name of the application created" + example: "My Voice App" + answer_url: + $ref: '#/components/schemas/CallbackUrl' + event_url: + $ref: '#/components/schemas/CallbackUrl' + type: + type: string + example: "voice" + description: "The type of application created" + + ContextNumberLinking: + type: object + properties: + country: + type: string + description: "The country of the number" + example: "GB" + account: + type: string + description: "Which account the number is associated with" + example: "abcdef01" + msisdn: + type: string + description: "The phone number linked/unlinked to your application" + example: "447000123456" + applicationId: + type: string + description: "UUID of the app the number is being linked/unlinked to" + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + + ContextNumberUpdate: + type: object + properties: + country: + type: string + description: "The country of the number" + msisdn: + type: string + description: "The phone number linked/unlinked to your application" + example: "447000123456" + voice-type: + type: string + enum: + - tel + - sip + - vxml + - app + example: "app" + description: "The type of endpoint the number has been forwarded to" + voice-value: + type: string + description: "The value of the endpoint the number has been forwarded to" + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + http: + type: string + format: url + description: "The URL of the endpoint the number has been forwarded to" + example: "https://example.org/my-app-callback" + + ErrorBadRequest: + type: object + properties: + status: + type: integer + example: 400 + description: The HTTP Status code of the error + error: + type: string + example: BadRequest + description: A human readable description of the error + message: + type: string + example: "This was a bad request." + description: A longer description of the error + + ErrorUnauthorized: + type: object + properties: + status: + type: integer + example: 401 + description: The HTTP Status code of the error + error: + type: string + example: Unauthorized + description: A human readable description of the error + message: + type: string + example: "Full authentication is required. JWT was not found in \"Authorization\" HTTP header." + description: A longer description of the error + + ErrorForbidden: + type: object + properties: + status: + type: integer + example: 403 + description: The HTTP Status code of the error + error: + type: string + example: Forbidden + description: A human readable description of the error + message: + type: string + example: "An example goes here" + description: A longer description of the error + + ErrorNotFound: + type: object + properties: + status: + type: integer + example: 404 + description: The HTTP Status code of the error + error: + type: string + example: "Not Found" + description: A human readable description of the error XXX + message: + type: string + example: "Event with provided id: aaaaaaaa-bbbb-cccc-dddd-0123456789ab was not found" + description: A longer description of the error YYY + + NoContent: + type: object + properties: + status: + type: integer + example: 204 + description: The HTTP Status code of the error + error: + type: string + example: "No Content" + description: A human readable description of the error AAA + message: + type: string + example: "No content" + description: A longer description of the error BBB + + securitySchemes: + basicAuth: + type: http + scheme: basic diff --git a/tests/resources/definitions/common/common_errors.yml b/tests/resources/definitions/common/common_errors.yml new file mode 100755 index 00000000..49a2b69b --- /dev/null +++ b/tests/resources/definitions/common/common_errors.yml @@ -0,0 +1,109 @@ +--- +components: + fields: + instance: + type: string + example: 797a8f199c45014ab7b08bfe9cc1c12c + responses: + DefaultError: + description: Default Error + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#TO_BE_UPDATED' + title: + type: string + example: THIS ERROR NEEDS REPLACING + detail: + type: string + example: THIS IS A PLACEHOLDER FOR OTHER ERRORS + instance: + $ref: '#/components/fields/instance' + BadCredentialsError: + description: Credential is missing or invalid + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#unauthorized' + title: + type: string + example: Invalid credentials supplied + detail: + type: string + example: You did not provide correct credentials. + instance: + $ref: '#/components/fields/instance' + NotFoundError: + description: Resource Not Found + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#not-found' + title: + type: string + example: Not Found + detail: + type: string + example: ID 'ABC123' does not exist, or you do not have access + instance: + $ref: '#/components/fields/instance' + InvalidRequestMethod: + description: Invalid Request Method + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#wrong-verb' + title: + type: string + example: Request method not allowed + detail: + type: string + example: Request method 'TRACE' not supported + instance: + $ref: '#/components/fields/instance' + InvalidAcceptHeader: + description: Invalid Accept Header + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#accept-header' + title: + type: string + example: Invalid Accept header + detail: + type: string + example: "Invalid Accept header provided. Must be one of the following: 'application/json'" + instance: + $ref: '#/components/fields/instance' + UnsupportedContentTypeHeader: + description: Unsupported Content Type Header + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#content-type-header' + title: + type: string + example: Unsupported Content-Type header + detail: + type: string + example: "Unsupported Content-Type header provided. Must be one of the following: 'application/json'" + instance: + $ref: '#/components/fields/instance' diff --git a/tests/resources/definitions/conversation.v2.yml b/tests/resources/definitions/conversation.v2.yml new file mode 100755 index 00000000..8c871707 --- /dev/null +++ b/tests/resources/definitions/conversation.v2.yml @@ -0,0 +1,660 @@ +openapi: 3.0.0 +servers: + - url: "https://api.nexmo.com/beta2" +info: + version: 0.2.0 + title: Nexmo Conversation API + description: "The Nexmo Conversation API enables you to build conversation features where communication can take place across multiple mediums including IP Messaging, PSTN Voice, SMS and WebRTC Audio and Video. The context of the conversations is maintained though each communication event taking place within a conversation, no matter the medium." + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: "https://developer.nexmo.com/" + x-label: Developer Preview +paths: + "/conversations": + get: + operationId: get-conversations + tags: + - conversation + summary: List Conversations + description: | + Please note that not all data is available in the list endpoint. Once + you've identified the conversation you need to work with, use the + [GET /conversations/:id](#get-conversation) endpoint to fetch all of the conversation details + parameters: + - $ref: "#/components/parameters/page_size" + - $ref: "#/components/parameters/order" + - $ref: "#/components/parameters/cursor" + - $ref: "#/components/parameters/date_start" + - $ref: "#/components/parameters/date_end" + responses: + "200": + description: OK + content: + application/json: + schema: + properties: + page_size: + type: integer + example: 10 + description: The number of results returned on this page. + _embedded: + type: object + x-nexmo-developer-collection-description-shown: true + properties: + data: + type: object + properties: + conversations: + type: array + description: List of conversations matching the provided filter + items: + $ref: "#/components/schemas/conversation_list" + _links: + type: object + properties: + first: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/conversations?order=desc&page_size=10" + self: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/conversations?order=desc&page_size=10&cursor=88b395c167da4d94e929705cbd63b82973771e7d390d274a58e301386d5762600a3ffd799bfb3fc5190c5a0d124cdd0fc72fe6e450506b18e4e2edf9fe84c7a0" + next: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/conversations?order=desc&page_size=10&cursor=88b395c167da4d94e929705cbd63b829a650e69a39197bfd4c949f4243f60dc4babb696afa404d2f44e7775e32b967f2a1a0bb8fb259c0999ba5a4e501eaab55" + prev: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/conversations?order=desc&page_size=10&cursor=069626a3de11d2ec900dff5042197bd75f1ce41dafc3f2b2481eb9151086e59aae9dba3e3a8858dc355232d499c310fbfbec43923ff657c0de8d49ffed9f7edb" + + "/users": + get: + operationId: get-users + summary: List Users + parameters: + - $ref: "#/components/parameters/page_size" + - $ref: "#/components/parameters/order" + - $ref: "#/components/parameters/cursor" + responses: + "200": + description: OK + content: + application/json: + schema: + properties: + page_size: + type: integer + example: 10 + description: The number of results returned on this page + _embedded: + type: object + x-nexmo-developer-collection-description-shown: true + properties: + data: + type: object + properties: + users: + type: array + description: List of users matching the provided filter + items: + $ref: "#/components/schemas/user_list" + _links: + type: object + properties: + first: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/users?order=desc&page_size=10" + self: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/users?order=desc&page_size=10&cursor=88b395c167da4d94e929705cbd63b82973771e7d390d274a58e301386d5762600a3ffd799bfb3fc5190c5a0d124cdd0fc72fe6e450506b18e4e2edf9fe84c7a0" + next: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/users?order=desc&page_size=10&cursor=88b395c167da4d94e929705cbd63b829a650e69a39197bfd4c949f4243f60dc4babb696afa404d2f44e7775e32b967f2a1a0bb8fb259c0999ba5a4e501eaab55" + prev: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/users?order=desc&page_size=10&cursor=069626a3de11d2ec900dff5042197bd75f1ce41dafc3f2b2481eb9151086e59aae9dba3e3a8858dc355232d499c310fbfbec43923ff657c0de8d49ffed9f7edb" + + "/conversations/{conversation_id}/members": + get: + operationId: get-members + tags: + - member + summary: List Members + parameters: + - $ref: "#/components/parameters/conversation_id_parameter" + - $ref: "#/components/parameters/page_size" + - $ref: "#/components/parameters/order" + - $ref: "#/components/parameters/cursor" + responses: + "200": + description: OK + content: + application/json: + schema: + properties: + page_size: + type: integer + example: 10 + description: The number of results returned on this page + _embedded: + type: object + x-nexmo-developer-collection-description-shown: true + properties: + data: + type: object + properties: + members: + type: array + description: List of members matching the provided filter + items: + $ref: "#/components/schemas/member_list" + _links: + type: object + properties: + first: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/members?order=desc&page_size=10" + self: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/members?order=desc&page_size=10&cursor=88b395c167da4d94e929705cbd63b82973771e7d390d274a58e301386d5762600a3ffd799bfb3fc5190c5a0d124cdd0fc72fe6e450506b18e4e2edf9fe84c7a0" + next: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/members?order=desc&page_size=10&cursor=88b395c167da4d94e929705cbd63b829a650e69a39197bfd4c949f4243f60dc4babb696afa404d2f44e7775e32b967f2a1a0bb8fb259c0999ba5a4e501eaab55" + prev: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/members?order=desc&page_size=10&cursor=069626a3de11d2ec900dff5042197bd75f1ce41dafc3f2b2481eb9151086e59aae9dba3e3a8858dc355232d499c310fbfbec43923ff657c0de8d49ffed9f7edb" + + "/conversations/{conversation_id}/events": + get: + operationId: get-events + tags: + - event + summary: List Events + parameters: + - $ref: "#/components/parameters/conversation_id_parameter" + - $ref: "#/components/parameters/page_size" + - $ref: "#/components/parameters/order" + - $ref: "#/components/parameters/cursor" + - $ref: "#/components/parameters/start_id_parameter" + - $ref: "#/components/parameters/end_id_parameter" + - $ref: "#/components/parameters/event_type_parameter" + responses: + "200": + description: OK + content: + application/json: + schema: + properties: + page_size: + type: integer + example: 10 + description: The number of results returned on this page. + _embedded: + type: object + x-nexmo-developer-collection-description-shown: true + properties: + data: + type: object + properties: + events: + type: array + description: List of events matching the provided filter + items: + $ref: "#/components/schemas/all_events" + _links: + type: object + properties: + first: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/conversations/CON-92a44c64-7e4e-485f-a0c4-1f2adfc44625/events?page_size=10" + self: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/conversations/CON-92a44c64-7e4e-485f-a0c4-1f2adfc44625/events?page_size=10&cursor=a30e3b7a3dcda1434f64bbb1a5fa489b" + next: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/conversations/CON-92a44c64-7e4e-485f-a0c4-1f2adfc44625/events?page_size=10&cursor=4db03d9254d1cdaecc7b1fc15b6bf1a81f3d3151191d784f1327893f8dc96416" + prev: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/conversations/CON-92a44c64-7e4e-485f-a0c4-1f2adfc44625/events?page_size=10&cursor=84963f79fd25785be9706bd38bfd30c264f71964fa4edc8d8b4dd5f30bbd9f7c" + +components: + parameters: + start_id_parameter: + name: start_id + in: query + schema: + type: string + example: 13 + required: false + description: The ID to start returning events at + + end_id_parameter: + name: end_id + in: query + schema: + type: string + example: 19 + required: false + description: The ID to end returning events at + + event_type_parameter: + name: event_type + in: query + schema: + type: string + example: text + required: false + description: The type of event to search for. Does not currently support custom events + + event_id_parameter: + name: event_id + in: path + schema: + type: integer + example: 9 + required: true + description: The ID of the event + + member_id_parameter: + name: member_id + in: path + schema: + type: string + example: MEM-e46d9542-752a-4786-8f12-25a2e623a793 + required: true + description: The ID of the member + + user_id_parameter: + name: user_id + in: path + schema: + type: string + example: USR-e46d9542-752a-4786-8f12-25a2e623a793 + required: true + description: The ID of the user + + conversation_id_parameter: + name: conversation_id + in: path + schema: + type: string + example: CON-afe887d8-d587-4280-9aae-dfa4c9227d5e + required: true + description: The ID of the conversation + + page_size: + name: page_size + in: query + description: The number of results returned per page. The default value is `10`. The maximum value is `100`. + schema: + type: integer + default: 10 + maximum: 100 + required: false + order: + name: order + in: query + description: Show the most (`desc`) / least (`asc`) recently created entries first + schema: + type: string + default: asc + enum: + - asc + - desc + required: false + cursor: + name: cursor + in: query + description: | + The cursor to start returning results from. + + You are not expected to provide this manually, but to follow the url provided in `_links.next.href` in the response which contains a `cursor` value + schema: + type: string + required: false + + date_start: + name: date_start + in: query + description: Search for conversations created after this ISO8601 date + schema: + type: string + required: false + + date_end: + name: date_end + in: query + description: Search for conversations created before this ISO8601 date + schema: + type: string + required: false + + schemas: + all_events: + anyOf: + - $ref: "#/components/schemas/text_event" + - $ref: "#/components/schemas/custom_event" + - $ref: "#/components/schemas/member_invited_event" + - $ref: "#/components/schemas/member_left_event" + + text_event: + allOf: + - description: Text + x-tab-id: text-event + properties: + body: + type: object + description: The body of the `text` event + properties: + text: + type: string + description: The text sent in this event + example: Hello World + type: + type: string + description: The event type (`text`) + example: text + conversation_id: + type: string + example: CON-92a44c64-7e4e-485f-a0c4-1f2adfc44625 + description: The ID of the Conversation that the member belongs to + - $ref: "#/components/schemas/event" + + custom_event: + allOf: + - description: Custom + x-tab-id: custom-event + properties: + body: + type: object + description: The body of your `custom` event + example: { "my": "Custom Data" } + type: + type: string + description: The event type (`custom:`) + example: "custom:my_event" + - $ref: "#/components/schemas/event" + + member_invited_event: + allOf: + - description: Member Invited + x-tab-id: member-invited + properties: + type: + type: string + description: The event type (`member:invited`) + example: "member:invited" + - $ref: "#/components/schemas/event" + - properties: + body: + $ref: "#/components/schemas/member" + + member_left_event: + allOf: + - description: Member Left + x-tab-id: member-left + properties: + type: + type: string + description: The event type (`member:left`) + example: "member:left" + - $ref: "#/components/schemas/event" + - properties: + body: + $ref: "#/components/schemas/member" + + event: + type: object + properties: + id: + type: integer + example: 9 + description: The ID of the event + from: + type: string + example: MEM-afe887d8-d587-4280-9aae-dfa4c9227d5e + description: The member ID of the sender + timestamp: + type: string + example: 2019-09-12T19:49:21.823Z + description: The time that the event happened + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/conversations/CON-92a44c64-7e4e-485f-a0c4-1f2adfc44625/events/9" + + member_list: + type: object + properties: + id: + type: string + example: MEM-afe887d8-d587-4280-9aae-dfa4c9227d5e + description: Member ID + name: + type: string + example: ashley + description: The name of the User + display_name: + type: string + example: Ashley Arthur + description: The display name of the User + user_id: + type: string + example: USR-2c52f0ec-7a48-4b52-9d47-df47482b2b7e + description: The ID of the User + state: + type: string + description: The state that the member is in for this conversation + example: JOINED + enum: + - INVITED + - JOINED + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + example: https://api.nexmo.com/beta2/conversations/CON-92a44c64-7e4e-485f-a0c4-1f2adfc44625/members/MEM-e784d5d1-dff2-424a-9de7-bc34f1901177 + + member: + allOf: + - $ref: "#/components/schemas/member_list" + - type: object + properties: + timestamp: + type: object + properties: + invited: + type: string + example: "2019-09-03T18:40:24.324Z" + description: The time that an invitation was sent + joined: + type: string + example: "2019-09-12T16:27:07.450Z" + description: The time that the conversation was joined + left: + type: string + example: "2019-09-13T02:16:55.390Z" + description: The time that the member left the conversation + channel: + type: object + properties: + type: + type: string + example: app + description: The channel that the member joins with + enum: + - app + initiator: + type: object + properties: + invited: + type: object + properties: + is_system: + type: boolean + example: true + joined: + type: object + properties: + is_system: + type: boolean + example: true + media: + type: object + description: The current media state for the member + properties: + audio_settings: + description: The current audio state for the member + type: object + properties: + enabled: + type: boolean + example: false + description: Is audio enabled? + earmuffed: + type: boolean + example: false + description: Can the member hear other participants? + muted: + type: boolean + example: false + description: Can the member speak to other participants? + + conversation_list: + type: object + properties: + id: + type: string + example: CON-afe887d8-d587-4280-9aae-dfa4c9227d5e + description: The ID of the conversation + name: + $ref: "#/components/schemas/conversation_name" + display_name: + $ref: "#/components/schemas/conversation_display_name" + image_url: + $ref: "#/components/schemas/conversation_image_url" + timestamp: + type: object + properties: + created: + type: string + example: "2019-09-03T18:40:24.324Z" + description: The time that the conversation was created + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta2/conversations/CON-c4724477-72ac-438e-9fc0-1d3e2ff8728c" + + conversation_id: + type: string + example: CON-c4724477-72ac-438e-9fc0-1d3e2ff8728c + description: Automatically generated conversation ID + conversation_name: + type: string + example: my-conversation + description: Your internal conversation name. Must be unique + conversation_display_name: + type: string + example: Conversation with Ashley + description: The public facing name of the conversation + conversation_image_url: + type: string + example: https://example.com/my-image.png + description: An image URL that you associate with the conversation + + user_list: + type: object + properties: + id: + $ref: "#/components/schemas/user_id" + name: + $ref: "#/components/schemas/user_name" + _links: + $ref: "#/components/schemas/user_links" + + user_id: + type: string + description: The ID of the user + example: USR-e46d9542-752a-4786-8f12-25a2e623a793 + user_name: + type: string + description: The name of the user + example: ashley + user_links: + type: object + properties: + self: + type: object + properties: + href: + type: string + example: https://api.nexmo.com/beta2/users/USR-e46d9542-752a-4786-8f12-25a2e623a793 + +tags: + - name: conversation + description: A conversation is a shared core component that Nexmo APIs rely on. Conversations happen over multiple mediums and and can have associated Users through Memberships. + - name: member + description: Memberships connect users with conversations. Each membership has one conversation and one user however a user can have many memberships to conversations just as conversations can have many members. + - name: event + description: 'Events are actions that occur within a conversation. Examples of this includes: Text events from members, or invite events from users' diff --git a/tests/resources/definitions/conversation.yml b/tests/resources/definitions/conversation.yml new file mode 100755 index 00000000..cb88a001 --- /dev/null +++ b/tests/resources/definitions/conversation.yml @@ -0,0 +1,1229 @@ +openapi: 3.0.0 +servers: + - url: 'https://api.nexmo.com/beta' +info: + version: 1.7.5 + title: Nexmo Conversation API + description: 'The Nexmo Conversation API enables you to build conversation features where communication can take place across multiple mediums including IP Messaging, PSTN Voice, SMS and WebRTC Audio and Video. The context of the conversations is maintained though each communication event taking place within a conversation, no matter the medium.' + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' + x-label: Developer Preview +paths: + /conversations: + post: + operationId: createConversation + tags: + - conversation + summary: Create a conversation + responses: + '200': + $ref: '#/components/responses/ConversationLite' + requestBody: + $ref: '#/components/requestBodies/Conversation' + get: + operationId: listConversations + tags: + - conversation + summary: List conversations + description: > +
+ +
+

This API endpoint is deprecated. Please use /beta2/conversations.
This endpoint will return a maximum of 100 items.

+
+
+ + List all conversations associated with your application. + This endpoint required an admin JWT. To find all conversations for + the currently logged in user, see [GET /users/:id/conversations](#getuserConversations) + parameters: + - $ref: '#/components/parameters/date_start' + - $ref: '#/components/parameters/date_end' + - $ref: '#/components/parameters/page_size' + - $ref: '#/components/parameters/record_index' + - $ref: '#/components/parameters/order' + responses: + '200': + description: List Conversations Response Payload Object. + content: + application/json: + schema: + type: object + properties: + count: + type: number + example: '100' + description: The total number of records returned by your request. + page_size: + $ref: '#/components/schemas/page_size' + record_index: + $ref: '#/components/schemas/record_index' + _links: + $ref: '#/components/schemas/_links_conversations_list' + _embedded: + description: 'A list of conversation objects. See the [get details of a specific conversation](#retrieveConversation) response fields for a description of the nested objects' + type: object + properties: + conversations: + type: array + items: + type: object + properties: + uuid: + $ref: '#/components/schemas/conversation_id' + name: + $ref: '#/components/schemas/name_conversation' + href: + $ref: '#/components/schemas/href_conversation' + required: + - uuid + - name + - href + required: + - conversations + required: + - count + - page_size + - record_index + - _links + - _embedded + '/conversations/{conversation_id}': + parameters: + - $ref: '#/components/parameters/conversation_id' + put: + operationId: replaceConversation + tags: + - conversation + summary: Update a conversation + responses: + '200': + $ref: '#/components/responses/ConversationLite' + requestBody: + $ref: '#/components/requestBodies/Conversation' + get: + operationId: retrieveConversation + tags: + - conversation + summary: Retrieve a conversation + responses: + '200': + description: Retrieve a conversation + content: + application/json: + schema: + type: object + description: Conversation Object + properties: + uuid: + $ref: '#/components/schemas/conversation_id' + name: + $ref: '#/components/schemas/name_conversation' + numbers: + type: object + properties: {} + properties: + type: object + properties: + video: + type: boolean + example: false + display_name: + $ref: '#/components/schemas/display_name' + timestamp: + $ref: '#/components/schemas/timestamp_res_conversation' + sequence_number: + type: string + example: '1' + description: 'The last Event ID in this conversation. This ID can be used to [retrieve a specific event](#getEvent)' + members: + type: array + description: Users associated to this conversation as members + items: + type: object + properties: + member_id: + $ref: '#/components/schemas/member_id' + user_id: + $ref: '#/components/schemas/user_id' + name: + $ref: '#/components/schemas/name_user' + state: + $ref: '#/components/schemas/member_state' + timestamp: + $ref: '#/components/schemas/timestamp_res_member' + initiator: + $ref: '#/components/schemas/initiator' + channel: + $ref: '#/components/schemas/channel' + api_key: + type: string + description: The API key for your account + _links: + $ref: '#/components/schemas/_links_conversation' + required: + - uuid + delete: + operationId: deleteConversation + tags: + - conversation + summary: Delete a conversation + responses: + '200': + $ref: '#/components/responses/SuccessEmptyJSON' + '/conversations/{conversation_id}/record': + servers: + - url: 'https://api.nexmo.com/v1' + description: Override base path for the PUT /conversations/{conversation_id}/record operation + parameters: + - $ref: '#/components/parameters/conversation_id' + put: + operationId: recordConversation + tags: + - conversation + summary: Record a conversation + responses: + '204': + description: 'No Content' + '404': + description: 'Not Found' + '400': + description: 'Bad Request' + requestBody: + $ref: '#/components/requestBodies/RecordConversation' + '/conversations/{conversation_id}/events': + parameters: + - $ref: '#/components/parameters/conversation_id' + post: + operationId: createEvent + tags: + - event + summary: Create an event + requestBody: + content: + application/json: + schema: + type: object + description: Create New Event Request Payload Object + properties: + type: + $ref: '#/components/schemas/event_type' + to: + $ref: '#/components/schemas/member_id' + from: + $ref: '#/components/schemas/member_id' + body: + $ref: '#/components/schemas/event_body' + required: + - type + - from + responses: + '201': + description: Create New Event Response Payload Object + content: + application/json: + schema: + type: object + description: Create New Event Response Payload Object + properties: + id: + $ref: '#/components/schemas/event_id' + timestamp: + $ref: '#/components/schemas/timestamp_res_event' + href: + $ref: '#/components/schemas/href_event' + get: + operationId: getEvents + description: > +
+ +
+

This API endpoint is deprecated. Please use /beta2/events
This endpoint will return a maximum of 100 items.

+
+
+ + tags: + - event + summary: List events + responses: + '200': + description: Retrieve Events Response Payload Object + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/event_retrieved' + '/conversations/{conversation_id}/events/{event_id}': + parameters: + - $ref: '#/components/parameters/conversation_id' + - $ref: '#/components/parameters/event_id' + get: + operationId: getEvent + tags: + - event + summary: Retrieve an event + responses: + '200': + description: Retrieve an event Content Payload + content: + application/json: + schema: + $ref: '#/components/schemas/event_retrieved' + delete: + operationId: deleteEvent + tags: + - event + summary: Delete an event + responses: + '200': + $ref: '#/components/responses/SuccessEmptyJSON' + '/conversations/{conversation_id}/members': + parameters: + - $ref: '#/components/parameters/conversation_id' + get: + operationId: getMembers + description: > +
+ +
+

This API endpoint is deprecated. Please use /beta2/members
This endpoint will return a maximum of 100 items.

+
+
+ tags: + - member + summary: List members + responses: + '200': + description: Members List Object + content: + application/json: + schema: + type: array + items: + type: object + properties: + user_id: + $ref: '#/components/schemas/user_id' + user_name: + $ref: '#/components/schemas/name_user' + name: + $ref: '#/components/schemas/name_user' + state: + $ref: '#/components/schemas/member_state' + required: + - user_id + - name + - user_name + - state + post: + operationId: createMember + tags: + - member + summary: Create a member + responses: + '201': + description: 'Create or invite Member in invite state ' + content: + application/json: + schema: + type: object + properties: + id: + $ref: '#/components/schemas/member_id' + user_id: + $ref: '#/components/schemas/user_id' + state: + $ref: '#/components/schemas/member_state' + timestamp: + $ref: '#/components/schemas/timestamp_res_member' + channel: + $ref: '#/components/schemas/channel' + href: + $ref: '#/components/schemas/href_member' + initiator: + $ref: '#/components/schemas/initiator' + requestBody: + content: + application/json: + schema: + type: object + description: 'Create a Member in invite state ' + required: + - user_id + - channel + properties: + action: + $ref: '#/components/schemas/member_action' + user_id: + $ref: '#/components/schemas/user_id' + member_id: + $ref: '#/components/schemas/member_id' + channel: + $ref: '#/components/schemas/channel' + media: + $ref: '#/components/schemas/media' + knocking_id: + $ref: '#/components/schemas/knocker_id' + member_id_inviting: + $ref: '#/components/schemas/member_id_inviting' + '/conversations/{conversation_id}/members/{member_id}': + parameters: + - $ref: '#/components/parameters/conversation_id' + - $ref: '#/components/parameters/member_id' + get: + operationId: getMember + tags: + - member + summary: Retrieve a member + responses: + '200': + description: Retrieve member payload + content: + application/json: + schema: + properties: + id: + $ref: '#/components/schemas/member_id' + href: + $ref: '#/components/schemas/href_member' + put: + operationId: updateMember + tags: + - member + summary: Update a member + responses: + '200': + description: Member retrieved + content: + application/json: + schema: + properties: + id: + $ref: '#/components/schemas/member_id' + href: + $ref: '#/components/schemas/href_member' + requestBody: + content: + application/json: + schema: + properties: + action: + $ref: '#/components/schemas/member_action' + channel: + $ref: '#/components/schemas/channel' + delete: + operationId: deleteMember + tags: + - member + summary: Delete a member + responses: + '200': + $ref: '#/components/responses/SuccessEmptyJSON' + /users: + get: + operationId: getUsers + description: > +
+ +
+ This API endpoint is deprecated. Please use /beta2/users
This endpoint will return a maximum of 100 items.
+
+
+ tags: + - user + summary: List users + responses: + '200': + description: List of users + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + $ref: '#/components/schemas/user_id' + name: + $ref: '#/components/schemas/name_user' + href: + $ref: '#/components/schemas/href_user' + post: + operationId: createUser + tags: + - user + summary: Create a user + description: 'Note: Users must be created with an admin JWT.' + responses: + '200': + description: Create a user response + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/name_user' + display_name: + $ref: '#/components/schemas/display_name_user' + href: + $ref: '#/components/schemas/href_user' + requestBody: + content: + application/json: + schema: + type: object + description: 'Create a Member in invite state ' + properties: + name: + $ref: '#/components/schemas/name_user' + display_name: + $ref: '#/components/schemas/display_name_user' + image_url: + $ref: '#/components/schemas/image_url' + '/users/{user_id}': + parameters: + - $ref: '#/components/parameters/user_id' + get: + operationId: getUser + tags: + - user + summary: Retrieve a user + responses: + '200': + description: Retrieve a user + content: + application/json: + schema: + properties: + id: + $ref: '#/components/schemas/user_id' + name: + $ref: '#/components/schemas/name_user' + href: + type: string + example: 'https://api.nexmo.com/beta/users/USR-63f61863-4a51-4f6b-86e1-46edebio0391' + put: + operationId: updateUser + tags: + - user + summary: Update a user + responses: + '200': + description: Retrieve a user + content: + application/json: + schema: + properties: + id: + $ref: '#/components/schemas/user_id' + href: + $ref: '#/components/schemas/href_user' + requestBody: + content: + application/json: + schema: + properties: + name: + $ref: '#/components/schemas/name_user' + display_name: + $ref: '#/components/schemas/display_name_user' + image_url: + $ref: '#/components/schemas/image_url' + channels: + $ref: '#/components/schemas/channel' + delete: + operationId: deleteUser + tags: + - user + summary: Delete a user + responses: + '200': + $ref: '#/components/responses/SuccessEmptyJSON' + '/users/{user_id}/conversations': + parameters: + - $ref: '#/components/parameters/user_id' + get: + operationId: getuserConversations + tags: + - user + summary: List user conversations + responses: + '200': + description: List user conversations + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + $ref: '#/components/schemas/name_conversation' + image_url: + $ref: '#/components/schemas/image_url' + display_name: + $ref: '#/components/schemas/display_name' + state: + $ref: '#/components/schemas/member_state' + member_id: + $ref: '#/components/schemas/member_id' + sequence_number: + type: integer + format: url + description: the id of the last event of the conversation (event's id is an incremental number + example: '123' + href: + $ref: '#/components/schemas/href' + id: + $ref: '#/components/schemas/conversation_id' + timestamp: + type: object + properties: + created: + $ref: '#/components/schemas/timestamp' + /legs: + get: + operationId: listLegs + tags: + - leg + summary: List legs + responses: + '200': + description: List Legs Successfully + content: + application/json: + schema: + type: object + description: List Legs Response Payload Object + properties: + count: + type: number + example: '100' + description: The total number of records returned by your request. + page_size: + $ref: '#/components/schemas/page_size' + record_index: + $ref: '#/components/schemas/record_index' + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + required: + - href + required: + - self + _embedded: + description: 'A list of conversation objects. See the [get details of a specific conversation](#retrieveConversation) response fields for a description of the nested objects' + type: object + properties: + legs: + type: array + items: + type: object + properties: + uuid: + $ref: '#/components/schemas/leg_id' + type: + $ref: '#/components/schemas/channel_type' + conversation_uuid: + $ref: '#/components/schemas/conversation_id' + state: + $ref: '#/components/schemas/leg_state' + from: + type: object + to: + type: object + start_time: + $ref: '#/components/schemas/timestamp_leg_start_time' + start_end: + $ref: '#/components/schemas/timestamp_leg_end_time' + _links: + type: object + _embedded: + type: object + required: + - uuid + - name + - href + required: + - legs + required: + - count + - page_size + - record_index + - _links + - _embedded + example: + count: 1 + page_size: 10 + record_index: 0 + _links: + self: + href: 'https://api.nexmo.com/beta/legs' + _embedded: + legs: + - uuid: 2a71f8a1-b6f1-42b3-9eef-c69729e17513 + type: app + conversation_uuid: CON-511d076b-9d39-498c-baa7-b7a4bfbd6e32 + status: completed + from: + type: app + to: + type: app + start_time: '' + end_time: '' + rtc: + session_id: SES-84ebef0d-321a-47e6-8446-f4fcc5ca74b9 + state: terminated + _links: + self: + href: 'https://api.nexmo.com/beta/legs/2a71f8a1-b6f1-42b3-9eef-c69729e17513' + '/legs/{leg_id}': + parameters: + - $ref: '#/components/parameters/leg_id' + delete: + operationId: deleteLeg + tags: + - leg + summary: Delete a leg + responses: + '200': + $ref: '#/components/responses/SuccessEmptyJSON' +components: + schemas: + initiator: + type: object + properties: + joined: + type: object + properties: + isSystem: + type: boolean + description: '`true` if the user was invited by an admin JWT. `user_id` and `member_id` will not exist if `true`' + user_id: + $ref: '#/components/schemas/user_id' + member_id: + $ref: '#/components/schemas/member_id' + + action: + type: string + example: start + description: Recording Action + enum: + - start + - stop + event_url: + type: array + items: + type: string + format: url + example: '["https://example.com/event"]' + x-nexmo-developer-collection-description-shown: true + description: The webhook endpoint where recording progress events are sent to. + event_method: + type: string + description: The HTTP method used to send event information to event_url. + example: POST + default: POST + split: + type: string + description: Record the sent and received audio in separate channels of a stereo recording + example: conversation + default: conversation + format: + type: string + description: Record the Conversation in a specific format. + example: mp3 + default: mp3 + enum: + - mp3 + - wav + leg_state: + type: string + example: terminated + description: Leg Status + enum: + - terminated + conversation_id: + type: string + example: CON-63f61863-4a51-4f6b-86e1-46edebio0391 + description: The unique identifier for this conversation + member_id: + type: string + example: MEM-63f61863-4a51-4f6b-86e1-46edebio0391 + description: Member ID + member_id_inviting: + type: string + example: MEM-63f61863-4a51-4f6b-86e1-46edebio0391 + description: Member ID of the member that sends the invitation + member_action: + type: string + description: Invite or join a member to a conversation + example: join + enum: + - invite + - join + user_id_or_user_name: + type: string + description: user name or user id of the inviter + example: someone_else_user_name + user_id: + type: string + example: USR-63f61863-4a51-4f6b-86e1-46edebio0391 + description: User ID + name: + type: string + example: my_unique_name + description: Unique name + name_conversation: + type: string + example: customer_chat + description: Unique name for a conversation + name_user: + type: string + example: my_user_name + description: Unique name for a user + href: + type: string + format: url + example: 'https://api.nexmo.com/beta/converations/CON-63f61863-4a51-4f6b-86e1-46edebio0391' + description: A link towards a resources included in Conversation API + href_conversation: + type: string + format: url + example: 'https://api.nexmo.com/beta/converations/CON-63f61863-4a51-4f6b-86e1-46edebio0391' + description: A link towards a conversation included in Conversation API + href_conversations_list: + type: string + format: url + example: 'https://api.nexmo.com/beta/converations?page_size=2&record_index=10&' + description: A link towards a conversations list included in Conversation API + href_member: + type: string + format: url + example: 'https://api.nexmo.com/beta/converations/CON-63f61863-4a51-4f6b-86e1-46edebio0391/members/MEM-63f61863-4a51-4f6b-86e1-46edebio0391' + description: A link towards a member included in Conversation API + href_user: + type: string + format: url + example: 'https://api.nexmo.com/beta/converations/USR-63f61863-4a51-4f6b-86e1-46edebio0391' + description: A link towards a user included in Conversation API + href_event: + type: string + format: url + example: 'https://api.nexmo.com/beta/converations/CON-63f61863-4a51-4f6b-86e1-46edebio0391/events/1' + description: A link towards a conversation event included in Conversation API + href_rtc: + type: string + format: url + example: 'https://api.nexmo.com/beta/converations/CON-63f61863-4a51-4f6b-86e1-46edebio0391/rtc/7777777777777' + description: A link towards a rtc (leg) included in Conversation API + event_id: + type: string + example: '5' + description: Event id. This is a progressive integer + image_url: + type: string + format: url + example: 'https://example.com/image.png' + description: A link to an image for conversations' and users' avatars + display_name: + type: string + example: Customer Chat + description: The display name for the conversation. It does not have to be unique + display_name_user: + type: string + example: My User Name + description: A string to be displayed as user name. It does not need to be unique + numbers: + type: object + description: 'An object containing number on different channels. ' + example: + pstn: 14155550100 + sms: 442079460000 + properties: + sms: + type: string + description: phone number used for sms channel + example: 442079460000 + pstn: + type: string + description: phone number used for pstn channel + example: 14155550100 + conversation_properties: + type: object + description: Conversation properties + properties: + ttl: + type: number + example: 60 + description: 'Time to leave. After how many seconds an empty conversation is deleted.' + _links_conversations_list: + type: object + description: 'A series of links between resources in this API in the http://stateless.co/hal_specification.html.' + properties: + self: + type: object + properties: + href: + $ref: '#/components/schemas/href_conversations_list' + required: + - href + required: + - self + _links_conversation: + type: object + properties: + self: + type: object + properties: + href: + $ref: '#/components/schemas/href_conversation' + page_size: + type: number + description: The amount of records returned in this response + minimum: 1 + maximum: 100 + default: 10 + record_index: + type: number + description: 'Return `page_size` amount of conversations from this index in the response. That is, if your request returns 300 conversations, set `record_index` to 5 in order to return conversations 50 to 59. The default value is 0. That is, the first `page_size` calls.' + default: 0 + minimum: 0 + timestamp: + type: string + example: '2020-01-01T14:00:00.00Z' + description: Timestamp + timestamp_created: + type: string + example: '2020-01-01T14:00:00.00Z' + description: Time of creation + timestamp_updated: + type: string + example: '2020-01-01T14:05:00.00Z' + description: Time of last update + timestamp_destroyed: + type: string + example: '2020-01-01T14:20:00.00Z' + description: Time of last update + timestamp_leg_start_time: + type: string + example: '2020-01-01T14:00:00.00Z' + description: Time of leg start + timestamp_leg_end_time: + type: string + example: '2020-01-01T14:00:00.00Z' + description: Time of leg end + timestamp_res_event: + type: string + example: '2020-01-01T14:00:00.00Z' + description: Time of event creation + timestamp_res_conversation: + type: object + properties: + created: + $ref: '#/components/schemas/timestamp_created' + updated: + $ref: '#/components/schemas/timestamp_updated' + destroyed: + $ref: '#/components/schemas/timestamp_destroyed' + timestamp_res_member: + type: object + properties: + invited: + $ref: '#/components/schemas/timestamp' + joined: + $ref: '#/components/schemas/timestamp' + left: + $ref: '#/components/schemas/timestamp' + timestamp_obj_leg: + type: object + properties: + start: + $ref: '#/components/schemas/timestamp_created' + end: + $ref: '#/components/schemas/timestamp' + request: + $ref: '#/components/schemas/timestamp' + member_state: + type: string + description: 'The state that the member is in. Possible values are `invited`, `joined`, `left`, or `unknown`' + example: invited + enum: + - invited + - joined + - left + - unknown + leg_id: + type: string + example: a595959595959595995 + description: The id of the leg. rtc_id and call_id are leg id + channel_number: + type: string + example: a447700900585 + description: this can be a phone number or a random string + channel_type: + type: string + example: phone + description: Channel type + enum: + - app + - phone + - sip + - websocket + - vbc + channel: + type: object + description: 'A user who joins a conversation as a member can have one channel per membership type. Channels can be `app`, `phone`, `sip`, `websocket`, or `vbc`' + properties: + type: + $ref: '#/components/schemas/channel_type' + leg_id: + $ref: '#/components/schemas/leg_id' + from: + oneOf: + - "$ref": "voice.yml#/components/schemas/EndpointApp" + - "$ref": "voice.yml#/components/schemas/EndpointPhone" + - "$ref": "voice.yml#/components/schemas/EndpointSip" + - "$ref": "voice.yml#/components/schemas/EndpointWebsocket" + - "$ref": "voice.yml#/components/schemas/EndpointVBCExtension" + to: + oneOf: + - "$ref": "voice.yml#/components/schemas/EndpointApp" + - "$ref": "voice.yml#/components/schemas/EndpointPhone" + - "$ref": "voice.yml#/components/schemas/EndpointSip" + - "$ref": "voice.yml#/components/schemas/EndpointWebsocket" + - "$ref": "voice.yml#/components/schemas/EndpointVBCExtension" + leg_ids: + type: array + description: Leg ids associated with this Channel. The first item in the array represents the main active Leg. The second item, if exists, represents a screen-share Leg. + items: + properties: + leg_id: + $ref: '#/components/schemas/leg_id' + media: + type: object + description: Media Object + properties: {} + example: + audio_settings: + enabled: false + earmuffed: false + muted: false + event_type: + type: string + description: Event type + example: text + event_body: + type: object + description: Event Body + example: + text: My Text + event_retrieved: + type: object + description: Retrieve Events Response Payload Object Item + properties: + id: + $ref: '#/components/schemas/event_id' + type: + $ref: '#/components/schemas/event_type' + from: + $ref: '#/components/schemas/member_id' + to: + $ref: '#/components/schemas/member_id' + body: + $ref: '#/components/schemas/event_body' + state: + $ref: '#/components/schemas/member_state' + timestamp: + $ref: '#/components/schemas/timestamp_created' + href: + $ref: '#/components/schemas/href_event' + required: + - id + - type + - body + - timestamp + - href + knocker_id: + type: string + description: Knocker ID. A knocker is a pre-member of a conversation who does not exist yet + example: a972836a-450f-35fa-156c-52a2ab5b7d25 + parameters: + leg_id: + name: leg_id + in: path + required: true + description: Leg ID + schema: + type: string + user_id: + name: user_id + in: path + required: true + description: User ID + schema: + type: string + conversation_id: + name: conversation_id + in: path + required: true + description: Conversation ID + example: CON-f972836a-550f-45fa-956c-12a2ab5b7d22 + schema: + type: string + member_id: + name: member_id + in: path + required: true + description: Member ID + schema: + type: string + event_id: + name: event_id + in: path + description: Event ID + required: true + schema: + type: string + date_start: + name: date_start + in: query + required: false + description: Return the records that occurred after this point in time. + schema: + example: '2018-01-01 10:00:00' + type: string + format: dateTime + date_end: + name: date_end + in: query + required: false + description: Return the records that occurred before this point in time. + schema: + example: '2018-01-01 12:00:00' + type: string + format: dateTime + page_size: + name: page_size + in: query + description: Return this amount of records in the response + required: false + schema: + $ref: '#/components/schemas/page_size' + record_index: + name: record_index + in: query + description: Return calls from this index in the response + required: false + schema: + $ref: '#/components/schemas/record_index' + order: + name: order + in: query + description: Return the records in ascending or descending order. + required: false + schema: + type: string + default: asc + enum: + - asc + - desc + - ASC + - DESC + responses: + ConversationLite: + description: Create / Update Conversation Response Payload Object + content: + application/json: + schema: + type: object + properties: + id: + $ref: '#/components/schemas/conversation_id' + href: + $ref: '#/components/schemas/href_conversation' + required: + - id + - href + SuccessEmptyJSON: + description: Success response with empty JSON + content: + application/json: + schema: + type: object + description: Empty JSON payload + example: {} + requestBodies: + EmptyBody: + description: Conversation Request Payload Object + required: true + content: + application/json: + schema: + type: object + example: {} + Conversation: + description: Conversation Request Payload Object + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/name_conversation' + display_name: + $ref: '#/components/schemas/display_name' + image_url: + $ref: '#/components/schemas/image_url' + numbers: + $ref: '#/components/schemas/numbers' + properties: + $ref: '#/components/schemas/conversation_properties' + RecordConversation: + description: Record Conversation Request Payload Object + content: + application/json: + schema: + type: object + properties: + action: + $ref: '#/components/schemas/action' + event_url: + $ref: '#/components/schemas/event_url' + event_method: + $ref: '#/components/schemas/event_method' + split: + $ref: '#/components/schemas/split' + format: + $ref: '#/components/schemas/format' + required: + - action + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT +security: + - bearerAuth: [] + +tags: + - name: conversation + description: A conversation is a shared core component that Nexmo APIs rely on. Conversations happen over multiple mediums and and can have associated Users through Memberships. + - name: user + description: 'The concept of a user exists in Nexmo APIs, you can associate one with a user in your own application if you choose. A user can have multiple memberships to conversations and can communicate with other users through various different mediums.' + - name: member + description: Memberships connect users with conversations. Each membership has one conversation and one user however a user can have many memberships to conversations just as conversations can have many members. + - name: event + description: 'Events are actions that occur within a conversation. Examples of this includes: Text events from members, or invite events from users' + - name: leg + description: 'A leg can be a video call, IP call, or PSTN call that users participate in using multiple platforms. With this endpoint you can retrieve the details about all of the legs that took place in your application.' + - name: rtc + description: Rtc actions. Any rtc action related (like starting a new rtc connection). diff --git a/tests/resources/definitions/conversion.yml b/tests/resources/definitions/conversion.yml new file mode 100755 index 00000000..89fbe202 --- /dev/null +++ b/tests/resources/definitions/conversion.yml @@ -0,0 +1,165 @@ +openapi: 3.0.0 +info: + title: Nexmo Conversion API + version: 1.0.1 + description: >- + The Conversion API allows you to tell Nexmo about the reliability of your + 2FA communications. Sending conversion data back to us means that Nexmo can + deliver messages faster and more reliably. + + The conversion data you send us is confidential: Nexmo does not share it + with third parties. + + In order to identify the carriers who provide the best performance, Nexmo + continually tests the routes we use to deliver SMS and voice calls. Using + Adaptive Routing™, Nexmo actively reroutes messages through different + carrier routes and ensures faster and more reliable delivery for your + messages. The route choice is made using millions of real-time conversion + data points. + contact: + name: Nexmo.com + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' + x-twitter: Nexmo + termsOfService: 'https://www.nexmo.com/terms-of-use' + license: + name: The MIT License (MIT) + url: 'https://opensource.org/licenses/MIT' + x-logo: + url: 'https://twitter.com/Nexmo/profile_image?size=original' + x-apiClientRegistration: 'https://dashboard.nexmo.com/sign-up' +servers: + - url: 'https://api.nexmo.com/conversions' +externalDocs: + url: 'https://developer.nexmo.com/api/conversion' + x-sha1: 8ad8bc6b0c51af4ca458c13cfced6124783ab113 +security: + - apiKey: [] + apiSecret: [] + - apiKey: [] + apiSig: [] +tags: + - name: SMS Conversion + description: SMS Conversion Request + - name: Voice Conversion + description: Voice Conversion Request +paths: + /sms: + post: + operationId: smsConversion + summary: Tell Nexmo if your SMS message was successful + description: >- + Send a Conversion API request with information about the SMS message + identified by `message-id`. Nexmo uses your conversion data and internal + information about `message-id` to help improve our routing of messages + in the future. + tags: + - SMS Conversion + parameters: + - $ref: '#/components/parameters/message-id' + - $ref: '#/components/parameters/delivered' + - $ref: '#/components/parameters/timestamp' + responses: + '200': + description: OK + '401': + description: Wrong credentials + '402': + description: Conversion has not been enabled for your account + '420': + description: Invalid parameters + '423': + description: Invalid parameters + /voice: + post: + operationId: voiceConversion + summary: Tell Nexmo if your voice call was successful + description: >- + Send a Conversion API request with information about the Call or + Text-To-Speech identified by `message-id`. Nexmo uses your conversion + data and internal information about `message-id` to help improve our + routing of messages in the future. + tags: + - Voice Conversion + parameters: + - $ref: '#/components/parameters/message-id' + - $ref: '#/components/parameters/delivered' + - $ref: '#/components/parameters/timestamp' + responses: + '200': + description: OK + '401': + description: Wrong credentials + '402': + description: Conversion has not been enabled for your account + '420': + description: Invalid parameters + '423': + description: Invalid parameters +components: + parameters: + message-id: + name: message-id + required: true + in: query + description: >- + The ID you receive in the response to a request. * From the Verify API - + use the `event_id` in the response to Verify Check. * From the SMS API - + use the `message-id` * From the Text-To-Speech API - use the `call-id` * + From the Text-To-Speech-Prompt API - use the `call-id` + schema: + type: string + example: 00A0B0C0 + delivered: + name: delivered + required: true + in: query + description: >- + Set to _true_ if your user replied to the message you sent. Otherwise, + set to _false_. + + **Note**: for curl, use 0 and 1. + schema: + enum: + - true + - false + - 0 + - 1 + example: true + timestamp: + name: timestamp + required: true + in: query + description: >- + When the user completed your call-to-action (e.g. visited your website, + installed your app) in + [UTC±00:00](https://en.wikipedia.org/wiki/UTC%C2%B100:00) format: + _yyyy-MM-dd HH:mm:ss_. + + If you do not set this parameter, Nexmo uses the time it receives this + request. + schema: + type: string + example: '2020-01-01 12:00:00' + securitySchemes: + apiKey: + type: apiKey + name: api_key + in: query + description: >- + You can find your API key in your [account + overview](https://dashboard.nexmo.com/account-overview) + apiSecret: + type: apiKey + name: api_secret + in: query + description: >- + You can find your API secret in your [account + overview](https://dashboard.nexmo.com/account-overview) + apiSig: + type: apiKey + name: sig + in: query + description: >- + The hash of the request parameters in alphabetical order, a timestamp + and the signature secret. For example: `sig=SIGNATURE` diff --git a/tests/resources/definitions/developer/messages.yml b/tests/resources/definitions/developer/messages.yml new file mode 100755 index 00000000..3c3e268d --- /dev/null +++ b/tests/resources/definitions/developer/messages.yml @@ -0,0 +1,610 @@ +openapi: 3.0.0 +info: + title: Message Search API + version: 1.0.3 + description: >- + The Messages API lets you retrieve messages you have sent via the SMS API by + ID, as well as retrieve details of messages that were rejected. + contact: + name: Nexmo.com + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' + x-twitter: Nexmo + termsOfService: 'https://www.nexmo.com/terms-of-use' + license: + name: The MIT License (MIT) + url: 'https://opensource.org/licenses/MIT' + x-logo: + url: 'https://twitter.com/Nexmo/profile_image?size=original' + x-apiClientRegistration: 'https://dashboard.nexmo.com/sign-up' +servers: + - url: 'https://rest.nexmo.com/search' +externalDocs: + url: 'https://developer.nexmo.com/api/developer/messages' + x-sha1: d8836c374e2a7504bd2cd59e05fcee440f67cb44 +security: + - apiKey: [] + apiSecret: [] +paths: + /message: + get: + summary: Get a single message + description: >- + Retrieve information about a single message that you sent using SMS API + or that was received on your number. + operationId: searchMessage + parameters: + - name: id + in: query + required: true + description: The ID of the message you want to retrieve. + example: 00A0B0C0 + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/messageBase' + - $ref: '#/components/schemas/messageMT' + application/xml: + schema: + oneOf: + - $ref: '#/components/schemas/messageBase' + - $ref: '#/components/schemas/messageMT' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/errorJson' + application/xml: + schema: + $ref: '#/components/schemas/errorXml' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/error' + application/xml: + schema: + $ref: '#/components/schemas/error' + /messages: + get: + summary: Search messages + description: >- + Retrieve multiple messages that you sent using the SMS API or that were received + on your number. You may provide one or more `ids` are specified, or both `date` and `number` + to identify the messages. + operationId: searchMessages + parameters: + - name: ids + in: query + description: A message ID to search for. You can specify up to 10 message IDs to search for as query parameters. + example: 'ids=123456789&ids=987654321' + schema: + type: string + - name: date + in: query + description: >- + The date the request to SMS API was submitted in the following + format: `YYYY-MM-DD` + example: '2020-01-01' + schema: + type: string + format: date + - name: to + in: query + description: The phone number the message was sent to. + example: '447700900000' + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/messages' + application/xml: + schema: + $ref: '#/components/schemas/messages' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/errorJson' + application/xml: + schema: + $ref: '#/components/schemas/errorXml' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/error' + application/xml: + schema: + $ref: '#/components/schemas/error' + /rejections: + get: + summary: Search for rejections + description: >- + Search for messages that have been rejected by Nexmo. Messages rejected + by the carrier do not appear. + operationId: searchRejections + parameters: + - name: date + in: query + description: >- + The date the request to SMS API was submitted in the following + format: `YYYY-MM-DD` + required: true + example: '2020-01-01' + schema: + type: string + format: date + - name: to + in: query + description: The phone number the message was sent to. + required: true + example: '447700900000' + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/rejections' + application/xml: + schema: + $ref: '#/components/schemas/rejections' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/errorJson' + application/xml: + schema: + $ref: '#/components/schemas/errorXml' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/error' + application/xml: + schema: + $ref: '#/components/schemas/error' +components: + schemas: + messageBase: + type: object + description: Inbound (MO) + required: + - type + - message-id + - account-id + - network + - from + - to + - body + - date-received + xml: + name: message + properties: + type: + type: string + description: >- + The message type. `MT` (mobile terminated or outbound) or `MO` + (mobile originated or inbound) + example: MO + enum: + - MO + message-id: + type: string + description: The id of the message you sent. + example: 0A00000000ABCD00 + account-id: + type: string + description: Your API Key. + example: API_KEY + date-received: + type: string + description: >- + The date and time at UTC+0 when Platform received your request in + the following format: `YYYY-MM-DD HH:MM:SS`. + example: '2020-01-01 12:00:00' + network: + type: string + description: >- + The [MCCMNC](https://en.wikipedia.org/wiki/Mobile_Network_Code) for + the carrier who delivered the message. + example: '012345' + from: + type: string + description: >- + The sender ID the message was sent from. Could be a phone number or + name. + example: Nexmo + to: + type: string + description: The phone number the message was sent to. + example: 447700900000 + body: + type: string + description: The body of the message. + example: A text message sent using the Nexmo SMS API + messageMT: + description: Outbound (MT) + type: object + required: + - type + - message-id + - account-id + - network + - from + - to + - body + - date-received + - price + - final-status + - date-closed + - latency + xml: + name: message + properties: + type: + type: string + description: >- + The message type. `MT` (mobile terminated or outbound) or `MO` + (mobile originated or inbound) + example: MT + enum: + - MT + message-id: + $ref: '#/components/schemas/messageBase/properties/message-id' + account-id: + $ref: '#/components/schemas/messageBase/properties/account-id' + network: + $ref: '#/components/schemas/messageBase/properties/network' + from: + $ref: '#/components/schemas/messageBase/properties/from' + to: + $ref: '#/components/schemas/messageBase/properties/to' + body: + $ref: '#/components/schemas/messageBase/properties/body' + date-received: + $ref: '#/components/schemas/messageBase/properties/date-received' + price: + type: number + description: Price in Euros for a MT message. + example: 0.0333 + date-closed: + type: string + description: >- + The date and time at UTC+0 when Platform received the delivery + receipt from the carrier who delivered the MT message. This + parameter is in the following format `YYYY-MM-DD HH:MM:SS`. + format: date-time + example: '2020-01-01 12:00:00' + latency: + type: number + description: >- + The overall latency between `date-received` and `date-closed` in + milliseconds. + example: 3000 + client-ref: + type: string + maxLength: 40 + description: >- + The [internal + reference](https://developer.nexmo.com/api/sms#send-an-sms) you set + in the request. + example: my-personal-reference + status: + type: string + description: >- + A code that explains where the message is in the delivery process. + If status is not `delivered` check `error-code` for more + information. If status is `accepted` ignore the value of + `error-code`. + enum: + - delivered + - expired + - failed + - rejected + - accepted + - buffered + - unknown + x-ms-enum: + name: status + values: + - value: delivered + description: This message has been delivered to the phone number. + - value: expired + description: >- + The target carrier did not send a status in the 48 hours after + this message was delivered to them. + - value: failed + description: The target carrier failed to deliver this message. + - value: rejected + description: The target carrier rejected this message. + - value: accepted + description: The target carrier has accepted to send this message. + - value: buffered + description: This message is in the process of being delivered. + - value: unknown + description: The target carrier has returned an undocumented status code. + final-status: + type: string + description: The status of `message-id` at `date-closed`. + enum: + - DELIVRD + - EXPIRED + - UNDELIV + - REJECTD + - UNKNOWN + x-ms-enum: + name: final-status + values: + - value: DELIVRD + description: This message has been delivered to the phone number. + - value: EXPIRED + description: >- + The target carrier did not send a status in the 48 hours after + this message was delivered to them. + - value: UNDELIV + description: The target carrier failed to deliver this message. + - value: REJECTD + description: The target carrier rejected this message. + - value: UNKNOWN + description: The target carrier has returned an undocumented status code. + example: DELIVRD + error-code: + $ref: '#/components/schemas/error-code' + error-code-label: + $ref: '#/components/schemas/error-code-label' + messages: + type: object + properties: + count: + type: integer + description: The number of messages included in the response. + items: + type: array + description: The messages in the response. + xml: + wrapped: true + items: + type: object + xml: + name: message + oneOf: + - $ref: '#/components/schemas/messageBase' + - $ref: '#/components/schemas/messageMT' + rejections: + type: object + properties: + count: + type: integer + description: The number of messages included in the response. + items: + type: array + xml: + wrapped: true + items: + type: object + properties: + account-id: + $ref: '#/components/schemas/messageBase/properties/account-id' + from: + $ref: '#/components/schemas/messageBase/properties/from' + to: + $ref: '#/components/schemas/messageBase/properties/to' + date-received: + $ref: '#/components/schemas/messageBase/properties/date-received' + error-code: + $ref: '#/components/schemas/error-code' + error-code-label: + $ref: '#/components/schemas/error-code-label' + xml: + name: rejection + required: + - account-id + - from + - to + - date-received + - error-code + - error-code-label + error-code: + type: string + description: Will be set if the `status` is not `accepted`. + example: '0' + enum: + - '0' + - '1' + - '2' + - '3' + - '4' + - '5' + - '6' + - '7' + - '8' + - '9' + - '10' + - '11' + - '12' + - '13' + - '14' + - '15' + - '99' + - '400' + - '401' + x-ms-enum: + name: error-code + values: + - value: '0' + description: Delivered. + - value: '1' + description: >- + Unknown - either; An unknown error was received from the carrier + who tried to send this this message. _or_ Depending on the + carrier, that `to` is unknown. When you see this error, and + `status` is rejected, always check if `to` in your request was + valid. + - value: '2' + description: >- + Absent Subscriber Temporary - this message was not delivered + because `to` was temporarily unavailable. For example, the handset + used for `to` was out of coverage or switched off. This is a + temporary failure, retry later for a positive result. + - value: '3' + description: >- + Absent Subscriber Permanent - `to` is no longer active, you should + remove this phone number from your database. + - value: '4' + description: >- + Call barred by user - you should remove this phone number from + your database. If the user wants to receive messages from you, + they need to contact their carrier directly. + - value: '5' + description: >- + Portability Error - there is an issue after the user has changed + carrier for `to` If the user wants to receive messages from you, + they need to contact their carrier directly. + - value: '6' + description: >- + Anti-Spam Rejection - carriers often apply restrictions that block + messages following different criteria. For example, on SenderID or + message content. + - value: '7' + description: >- + Handset Busy - the handset associated with `to` was not available + when this message was sent. If `status` is `Failed` this is a + temporary failure; retry later for a positive result. If `status` + is `Accepted` this message has is in the retry scheme and will be + resent until it expires in 24-48 hours. + - value: '8' + description: >- + Network Error - a network failure while sending your message. This + is a temporary failure, retry later for a positive result. + - value: '9' + description: >- + Illegal Number - you tried to send a message to a blacklisted + phone number. That is, the user has already sent a STOP opt-out + message and no longer wishes to receive messages from you. + - value: '10' + description: >- + Invalid Message - the message could not be sent because one of the + parameters in the message was incorrect. For example, incorrect + `type` or `udh` + - value: '11' + description: >- + Unroutable - the chosen route to send your message is not + available. This is because the phone number is either; Currently + on an unsupported _or_ network. On a pre-paid or reseller account + that could not receive a message sent by `from` To resolve this + issue either email us at + [support@nexmo.com](mailto:support@nexmo.com) or create a helpdesk + ticket at [https://help.nexmo.com](https://help.nexmo.com). + - value: '12' + description: >- + Destination unreachable - the message could not be delivered to + the phone number. + - value: '13' + description: >- + Subscriber Age Restriction - the carrier blocked this message + because the content is not suitable for `to` based on age + restrictions. + - value: '14' + description: >- + Number Blocked by Carrier - the carrier blocked this message. This + could be due to several reasons. For example, `to` s plan does not + include SMS or the account is suspended. + - value: '15' + description: >- + Pre-Paid - Insufficent funds - `to`’s pre-paid account does not + have enough credit to receive the message. + - value: '99' + description: >- + General Error - there is a problem with the chosen route to send + your message. To resolve this issue either email us at + [support@nexmo.com](mailto:support@nexmo.com) or create a helpdesk + ticket at [https://help.nexmo.com](https://help.nexmo.com). + - value: '400' + description: wrong parameters + - value: '401' + description: authentication failed + error-code-label: + type: string + description: A text label to explain `error-code`. + example: Delivered + error: + type: object + properties: + error-code: + $ref: '#/components/schemas/error-code' + error-code-label: + $ref: '#/components/schemas/error-code-label' + errorJson: + type: object + properties: + type: + type: string + example: BAD_REQUEST + error_title: + type: string + example: Bad Request + invalid_parameters: + type: object + additionalProperties: + type: string + example: Is required. + errorXml: + type: object + xml: + name: error + properties: + type: + type: string + example: BAD_REQUEST + error_title: + type: string + example: Bad Request + invalid_parameters: + type: array + items: + type: object + xml: + name: entry + properties: + key: + type: string + example: date + value: + type: string + example: Is required. + securitySchemes: + apiKey: + type: apiKey + name: api_key + in: query + description: >- + You can find your API key in your [account + overview](https://dashboard.nexmo.com/account-overview) + apiSecret: + type: apiKey + name: api_secret + in: query + description: >- + You can find your API secret in your [account + overview](https://dashboard.nexmo.com/account-overview) diff --git a/tests/resources/definitions/dispatch.yml b/tests/resources/definitions/dispatch.yml new file mode 100755 index 00000000..1b36835a --- /dev/null +++ b/tests/resources/definitions/dispatch.yml @@ -0,0 +1,580 @@ +openapi: "3.0.0" +info: + version: 0.3.2 + title: Dispatch API + description: 'The Dispatch API enables the developer to specify a multiple message workflow. A workflow follows a template. The first one we are adding is the failover template. The failover template instructs the Messages API to first send a message to the specified channel. If that message fails immediately or if the condition_status is not reached within the given time period the next message is sent. The developer will also receive status webhooks from the messages resource for each delivery and read event. This API is currently in Beta.' + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' + x-label: Beta +servers: + - url: https://api.nexmo.com/v0.1/dispatch +paths: + /: + post: + security: + - bearerAuth: [] + - basicAuth: [] + summary: Create a workflow + operationId: createWorkflow + requestBody: + description: Please note that last message does not have failover attribute inside it. All other messages must contain a failover attribute. + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/createWorkflow' + responses: + '202': + description: Accepted + content: + application/json: + schema: + $ref: '#/components/schemas/Response' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + callbacks: + message-status: + '{$request.body#/callback}': + post: + summary: Message Status + operationId: message-status + x-example-path: '/webhooks/message-status' + description: status of the message read or delivered etc. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MessageStatus' + responses: + '200': + description: Your server returns this code if it accepts the callback + final-report: + '{$request.body#/callback}': + post: + summary: The Final Report + operationId: final-report + x-example-path: '/webhooks/final-report' + description: The final workflow callback is sent when The condition_status was met within the expiry_time. If we take the example API call above. If we received a delivered status at 300 seconds (within the expiry_time) the workflow would be marked as completed. We would not send an SMS. We would then send the final callback. The final message in the failover is delivered. If the message Errors on the last step we will send the final callback. Please note GET is not currently supported. You will notice we have an href to a resource in some of the callbacks. These will fail to load but we wanted to maintain the same structure so that we can seamlessly integrate GET later. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/finalReport' + responses: + '200': + description: Your server returns this code if it accepts the callback + +components: + + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + basicAuth: + type: http + scheme: basic + + schemas: + + createWorkflow: + type: object + properties: + template: + description: 'The template that the Dispatch API will execute. For the initial version of the API the only available value will be failover' + type: string + enum: + - failover + example: failover + workflow: + description: 'Contains a message object that must reflect the current /messages resource. All parameters within the content object reflect the /messages docs.' + type: array + items: + oneOf: + - $ref: '#/components/schemas/sendWithFailoverMessage' + - $ref: '#/components/schemas/sendMessage' + + ToProperty: + type: object + required: + - type + properties: + type: + type: string + description: The type of message that you want to send. + example: 'sms' + enum: + - sms + - viber_service_msg + - messenger + - whatsapp + id: + description: | + The ID of the message recipient. + + **Messenger**: This value should be the `from.id` value you received in the inbound messenger event. + + **SMS**: or **Viber**: or **WhatsApp** This value is not required. + type: string + minLength: 1 + maxLength: 50 + example: '0123456789012345' + number: + type: string + minLength: 1 + maxLength: 50 + example: '447700900000' + description: | + **SMS**: or **MMS**: or **Viber**: or **WhatsApp** The phone number of the message recipient in the [E.164](https://en.wikipedia.org/wiki/E.164) format. Don't use a leading + or 00 when entering a phone number, start with the country code, for example, 447700900000. + + **Messenger**: This value is not required. + + FromProperty: + type: object + required: + - type + properties: + type: + type: string + description: The type of message that you want to send. + example: sms + enum: + - sms + - viber_service_msg + - messenger + - whatsapp + id: + description: | + Your ID for the platform that you are sending from. + + **Messenger**: This value should be the `to.id` value you received in the inbound messenger event. + + **Viber**: This is your Service Message ID given to you by Nexmo Account Manager. To find out more please visit [nexmo.com/products/messages](https://www.nexmo.com/products/messages). + + **SMS**: **MMS**: or **WhatsApp** This value is not required. + type: string + minLength: 1 + maxLength: 50 + example: '0123456789012345' + number: + type: string + minLength: 1 + maxLength: 50 + example: '447700900000' + description: | + **SMS**: or **MMS**: The phone number of the message sender in the [E.164](https://en.wikipedia.org/wiki/E.164) format. + + **WhatsApp**: This is your WhatsApp Business Number given to you by Nexmo Account Manager. To find out more please visit [nexmo.com/products/messages](https://www.nexmo.com/products/messages). + + **Messenger**: or **Viber**: This value is not required. + + MessageProperty: + type: object + required: + - content + properties: + content: + type: object + properties: + type: + description: | + The type of message that you are sending. + + **Messenger**: supports `text`, `image`, `audio`, `video` and `file`. + + **Viber Service Messages**: supports `image` and `text`. + + **WhatsApp**: supports `template`, `text`, `image`, `audio`, `video` and `file`. + + **SMS**: supports `text`. + type: string + enum: + - text + - image + - audio + - video + - file + - template + - custom + example: 'text' + text: + description: | + The text of the message. + + **Messenger**: Is limited to 640 characters + + **SMS** or **Viber**: Is 1000 characters + + **WhatsApp**: is 4096 characters + type: string + minLength: 1 + maxLength: 4096 + example: 'Nexmo Verification code: 64873. Valid for 10 minutes.' + image: + $ref: '#/components/schemas/ImageProperty' + audio: + $ref: '#/components/schemas/AudioProperty' + video: + $ref: '#/components/schemas/VideoProperty' + file: + $ref: '#/components/schemas/FileProperty' + template: + $ref: '#/components/schemas/TemplateProperty' + viber_service_msg: + type: object + properties: + category: + description: 'The use of different category tags enables the business to send messages for different use cases. For Viber Service Messages the first message sent from a business to a user must be personal, informative and a targeted message - not promotional. By default Nexmo sends the `transaction` category to Viber Service Messages.' + type: string + example: 'transaction' + enum: + - transaction + - promotion + ttl: + description: 'Only valid for Viber Service Messages. Set the time-to-live of message to be delivered in seconds. i.e. if the message is not delivered in 600 seconds then delete the message.' + type: integer + example: 600 + minimum: 30 + maximum: 86400 + messenger: + type: object + properties: + category: + description: 'The use of different category tags enables the business to send messages for different use cases. For Facebook Messenger they need to comply with their [Messaging Types policy]( https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types). Nexmo maps our `category` to their `messaging_type`. If `message_tag` is used, then an additional `tag` for that type is mandatory. By default Nexmo sends the `response` category to Facebook Messenger.' + type: string + example: 'message_tag' + enum: + - response + - update + - message_tag + tag: + description: ‘A full list of the possible tags is available on [developers.facebook.com](https://developers.facebook.com/docs/messenger-platform/send-messages/message-tags)' + type: string + example: 'ticket_update' + whatsapp: + type: object + properties: + policy: + description: 'Please note that WhatsApp will deprecate `fallback` policy in January 2020. There are two policies that you can specify when sending a Message Template: `deterministic` and `fallback`. `deterministic` — Deliver the Message Template in exactly the language and locale asked for. `fallback` — Deliver the Message Template in the language that matches users language/locale setting on device. If one can not be found, deliver using the specified fallback language.' + type: string + example: 'deterministic' + enum: + - fallback + - deterministic + locale: + description: 'We are using the industry standard, BCP 47, for locales. So in your API call to the /messages API you will need to put “en-GB” and this will refer to the “en_GB” template for WhatsApp. A full list of the possible locales is available on [developers.facebook.com](https://developers.facebook.com/docs/whatsapp/message-templates/creation#translations).' + type: string + example: 'en-GB' + + FailoverProperty: + description: 'Please note that last message does not have failover attribute inside it. All other messages must contain a failover attribute.' + type: object + properties: + expiry_time: + description: 'In seconds. Minimum value is 15 and maximum value is 86,400 seconds (1 day).' + type: integer + example: 600 + minimum: 15 + maximum: 86400 + condition_status: + description: 'Set the status the message must reach in the expiry_time before failing over. Options are delivered or read.' + type: string + example: 'delivered' + enum: + - delivered + - read + + TimestampProperty: + type: string + format: ISO 8601 + description: The datetime of when the event occurred. + example: '2020-01-01T14:00:00.000Z' + + ImageProperty: + type: object + properties: + url: + type: string + description: 'The URL of the image attachment. `messenger` and `mms` supports .jpg, .jpeg, .png and .gif. `viber_service_msg` supports .jpg .jpeg, and .png. `whatsapp` supports .jpg .jpeg, and .png.' + minLength: 1 + maxLength: 2000 + example: 'https://example.com/image.jpg' + caption: + type: string + description: 'Additional text to accompany the image. Supported by WhatsApp and MMS.' + minLength: 1 + maxLength: 3000 + example: 'Additional text to accompany the image.' + AudioProperty: + type: object + properties: + url: + type: string + description: 'The URL of the audio attachment. `messenger` supports .mp3. `whatsapp` supports .aac, .m4a, .amr, .mp3 and .opus.' + minLength: 1 + maxLength: 2000 + example: 'https://example.com/audio.mp3' + VideoProperty: + type: object + properties: + url: + type: string + description: | + The URL of the video attachment. + + **messenger** supports .mp4 + + **whatsapp** supports .mp4 and .3gpp. Note, only H.264 video codec and AAC audio codec is supported. + minLength: 1 + maxLength: 2000 + example: 'https://example.com/video.mp4' + FileProperty: + type: object + properties: + url: + type: string + description: 'The URL of the file attachment. `messenger` supports a wide range of attachments including .zip, .csv and .pdf. `whatsapp` supports .pdf, .doc(x), .ppt(x) and .xls(x).' + minLength: 1 + maxLength: 2000 + example: 'https://example.com/file.zip' + caption: + type: string + description: 'Additional text to accompany the image. Supported by WhatsApp and MMS.' + minLength: 1 + maxLength: 3000 + example: 'Additional text to accompany the image.' + TemplateProperty: + type: object + properties: + name: + type: string + description: 'The name of the template.' + example: 'whatsapp:hsm:technology:nexmo:verify' + parameters: + type: array + items: + type: object + properties: + default: + type: string + description: 'The parameters are an array. The first value being {{1}} in the template.' + example: '1234' + + sendWithFailoverMessage: + type: object + description: Send With Failover Message + required: + - to + - from + - message + properties: + to: + $ref: '#/components/schemas/ToProperty' + from: + $ref: '#/components/schemas/FromProperty' + message: + $ref: '#/components/schemas/MessageProperty' + failover: + $ref: '#/components/schemas/FailoverProperty' + + sendMessage: + type: object + description: 'Send Message' + required: + - to + - from + - message + properties: + to: + $ref: '#/components/schemas/ToProperty' + from: + $ref: '#/components/schemas/FromProperty' + message: + $ref: '#/components/schemas/MessageProperty' + + Response: + required: + - dispatch_uuid + type: object + properties: + dispatch_uuid: + description: 'The parent ID for workflow request.' + type: string + example: 'aaaaaaaa-bbbb-cccc-dddd-0123456789ab' + + Error: + description: 'The error format is standardized to the 4xx/5xx range with a code and a human readable explanation.' + required: + - type + - title + - detail + - instance + type: object + properties: + type: + type: string + example: 'https://www.nexmo.com/messages/Errors#InvalidParams' + detail: + type: string + example: 'Your request parameters did not validate.' + instance: + type: string + example: + invalid_parameters: + type: object + properties: + name: + type: string + example: 'Invalid `from` parameter' + reason: + type: string + example: 'Invalid `from` parameter' + + MessageStatus: + description: 'The callbacks for the message status are the same as defined in the Messaging API. The only difference will be that dispatch_uuid and link will be appended.' + type: object + properties: + message_uuid: + type: string + example: 'aaaaaaaa-bbbb-cccc-dddd-0123456789ab' + to: + $ref: '#/components/schemas/ToProperty' + from: + $ref: '#/components/schemas/FromProperty' + timestamp: + $ref: '#/components/schemas/TimestampProperty' + status: + type: string + example: delivered + description: The status of the message. + enum: + - submitted + - delivered + - read + - rejected + - undeliverable + error: + type: object + properties: + code: + type: integer + example: 1300 + description: The error code. See [our errors list](https://developer.nexmo.com/api-errors/messages-olympus) for a list of possible errors + reason: + type: string + example: 'Not part of the provider network' + description: Text describing the error. See [our errors list](https://developer.nexmo.com/api-errors/messages-olympus) for a list of possible errors + usage: + type: object + properties: + currency: + type: string + example: EUR + description: The charge currency in ISO 4217 format. + enum: + - EUR + price: + type: string + example: '0.0333' + description: The charge amount as a stringified number. + _links: + type: object + properties: + workflow: + type: object + properties: + dispatch_uuid: + type: string + example: 'aaaaaaaa-bbbb-cccc-dddd-0123456789ab' + href: + description: 'Please note GET is not currently supported' + type: string + example: '/workflows/aaaaaaaa-bbbb-cccc-dddd-0123456789ab' + + finalReport: + type: object + properties: + dispatch_uuid: + type: string + example: 'aaaaaaaa-bbbb-cccc-dddd-0123456789ab' + template: + type: string + enum: + - failover + example: failover + status: + type: string + enum: + - completed + - error + example: completed + timestamp: + $ref: '#/components/schemas/TimestampProperty' + usage: + type: object + description: 'This is the total cost of your Workflow request. Please note if a preceding message in the workflow request is delivered after the final message in the workflow is delivered it may not reflect the true total cost of the workflow.' + x-nexmo-developer-collection-description-shown: true + properties: + price: + description: The charge amount as a stringified number. + type: string + example: "0.02" + currency: + description: The charge currency in ISO 4217 format. + type: string + enum: + - EUR + example: 'EUR' + _links: + type: object + properties: + messages: + type: array + items: + type: object + properties: + message_uuid: + type: string + example: 'aaaaaaaa-bbbb-cccc-dddd-0123456789ab' + href: + description: 'Please note GET is not currently supported' + type: string + example: 'aaaaaaaa-bbbb-cccc-dddd-0123456789ab' + channel: + type: string + enum: + - messenger + - viber_sevice_msg + - sms + - whatsapp + - mms + example: viber_service_msg + usage: + type: object + properties: + currency: + type: string + example: EUR + description: The charge currency in ISO 4217 format. + enum: + - EUR + price: + type: string + example: '0.0333' + description: The charge amount as a stringified number. + status: + type: string + enum: + - submitted + - delivered + - read + - rejected + - undeliverable diff --git a/tests/resources/definitions/external-accounts.yml b/tests/resources/definitions/external-accounts.yml new file mode 100755 index 00000000..8e7f5dcc --- /dev/null +++ b/tests/resources/definitions/external-accounts.yml @@ -0,0 +1,788 @@ +openapi: "3.0.0" +info: + version: 0.1.3 + title: External Accounts + description: 'The External Accounts API is used to manage accounts for Viber Service Messages, Facebook Messenger and Whatsapp for use in the [Messages](https://developer.nexmo.com/messages/overview) and [Dispatch](https://developer.nexmo.com/dispatch/overview) APIs.' + x-label: Developer Preview + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' +servers: +- url: https://api.nexmo.com/beta/chatapp-accounts + +paths: + /messenger: + post: + security: + - bearerAuth: [] + - basicAuth: [] + summary: Create a Messenger account + operationId: CreateMessengerAccount + tags: + - Facebook Messenger + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - external_id + - access_token + properties: + external_id: + type: string + example: "12345678" + description: This is the unique identifier within the provider's domain. In this case it is the Page ID for your Facebook Page. Go to your Facebook Page, click "Settings", click "Messenger platform " scroll down to "Messenger link" to find your Page ID. + access_token: + type: string + example: "myAccessToken" + description: This is the Facebook Page token. You can obtain the token using one of the following methods +
    +
  • Use our tool to link a page to your Nexmo account [https://messenger.nexmo.com](https://messenger.nexmo.com)
  • +
  • Following the official [token reference](https://developers.facebook.com/docs/pages/access-tokens/)
  • +
+ name: + type: string + example: "optionalName" # optional + description: Custom account name + applications: + type: array + items: + type: string + example: ["optionalApplicationId"] + minItems: 0 + maxItems: 1 + description: Contains a list of application IDs which are linked to the account. +
    +
  • There is just one application allowed per an account.
  • +
  • The application type must be type "messages".
  • +
+ For more information see [Application API spec](https://developer.nexmo.com/api/application.v2) + responses: + '201': + description: Created. + content: + application/json: + schema: + $ref: '#/components/schemas/MessengerAccountResponse' + '400': + description: Bad Request. + content: + application/json: + schema: + type: object + properties: + type: + type: string + example: "https://www.nexmo.com/messages/Errors#InvalidParams" + title: + type: string + example: "Your request parameters didn't validate." + detail: + type: string + example: "Found errors validating 1 of your submitted parameters." + instance: + type: string + example: null + invalid_params: + type: "array" + items: + type: "object" + properties: + name: + type: string + example: "external_id" + reason: + type: string + example: "Missing `external_id` field" + '401': + description: Unauthorized. + content: + application/json: + schema: + $ref: '#/components/schemas/401Response' + '403': + description: Forbidden. + content: + application/json: + schema: + $ref: '#/components/schemas/403Response' + /messenger/{external_id}: + get: + security: + - bearerAuth: [] + - basicAuth: [] + summary: Retrieve a Messenger account + operationId: GetMessengerAccount + tags: + - Facebook Messenger + parameters: + - in: path + name: external_id + schema: + type: string + required: true + description: External id of the account you want to retrieve. In this case it is the Facebook Page ID. + responses: + '200': + description: OK. + content: + application/json: + schema: + $ref: '#/components/schemas/MessengerAccountResponse' + '401': + description: Unauthorized. + content: + application/json: + schema: + $ref: '#/components/schemas/401Response' + '404': + description: Not Found. + patch: + security: + - bearerAuth: [] + - basicAuth: [] + summary: Update a Messenger account + operationId: UpdateMessengerAccount + tags: + - Facebook Messenger + parameters: + - in: path + name: external_id + schema: + type: string + required: true + description: External id of the account you want to update. In this case it is the Facebook Page ID. + requestBody: + description: Request body can contain any of the following + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + example: "newName" + description: The new account name + applications: + type: "array" + items: + type: string + example: ["newApplicationId"] + + access_token: + type: string + example: "updatedAccessToken" + responses: + '200': + description: OK. + content: + application/json: + + schema: + type: object + required: + - external_id + - api_key + - provider + - access_token + properties: + name: + type: string + example: "newName" + description: The account name + applications: + type: "array" + items: + type: string + example: ["newApplicationId"] + description: The array of associated application ids + external_id: + type: string + example: "12345678" + description: The external identifier for this account. In this case it is the Facebook Page ID. + api_key: + type: string + example: "abcd1234" + description: The external api key for this account + provider: + type: string + example: "messenger" + description: The provider (will be `messenger`). + access_token: + type: string + example: "updatedAccessToken" + description: The provider access token + '400': + description: Bad Request. + content: + application/json: + schema: + type: object + properties: + type: + type: string + example: "https://www.nexmo.com/messages/Errors#InvalidParams" + title: + type: string + example: "Your request parameters didn't validate." + detail: + type: string + example: "Found errors validating 1 of your submitted parameters." + instance: + type: string + example: null + invalid_params: + type: "array" + items: + type: "object" + properties: + name: + type: string + example: "external_id" + reason: + type: string + example: "'external_id' parameter cannot be changed" + '401': + description: Unauthorized. + content: + application/json: + schema: + $ref: '#/components/schemas/401Response' + '403': + description: Forbidden. + content: + application/json: + schema: + $ref: '#/components/schemas/403Response' + '404': + description: Not Found. + delete: + security: + - bearerAuth: [] + - basicAuth: [] + summary: Delete a Messenger account + operationId: DeleteMessengerAccount + tags: + - Facebook Messenger + parameters: + - in: path + name: external_id + schema: + type: string + required: true + description: External id of the account you want to delete. In this case it is the Facebook Page ID. + responses: + '204': + description: No Content. + '401': + description: Unauthorized. + content: + application/json: + schema: + $ref: '#/components/schemas/401Response' + '403': + description: Forbidden. + content: + application/json: + schema: + $ref: '#/components/schemas/403Response' + '404': + description: Not Found. + /viber_service_msg/{external_id}: + get: + security: + - bearerAuth: [] + - basicAuth: [] + summary: Retrieve a Viber Service Message account + operationId: GetVSMAccount + tags: + - Viber Service Message + parameters: + - in: path + name: external_id + schema: + type: string + required: true + description: External id of the account you want to retrieve. In this case it will be your Viber Service Message ID. + responses: + '200': + description: OK. + content: + application/json: + schema: + $ref: '#/components/schemas/VSMAccountResponse' + '401': + description: Unauthorized. + content: + application/json: + schema: + $ref: '#/components/schemas/401Response' + '404': + description: Not Found. + /whatsapp/{external_id}: + get: + security: + - bearerAuth: [] + - basicAuth: [] + summary: Retrieve a Whatsapp account + operationId: GetWAAccount + tags: + - WhatsApp + parameters: + - in: path + name: external_id + schema: + type: string + required: true + description: External id of the account you want to retrieve. In this case it will be the WhatsApp number. + responses: + '200': + description: OK. + content: + application/json: + schema: + $ref: '#/components/schemas/WAAccountResponse' + '401': + description: Unauthorized. + content: + application/json: + schema: + $ref: '#/components/schemas/401Response' + '404': + description: Not Found. + /: + get: + security: + - bearerAuth: [] + - basicAuth: [] + summary: Retrieve all accounts you own + operationId: GetAllAccounts + tags: + - Account + parameters: + - in: query + name: provider + schema: + type: string + enum: + - messenger + - viber_service_msg + - whatsapp + required: false + description: Filter by provider + - in: query + name: page_number + schema: + type: integer + example: 1 + default: 1 + required: false + description: Page number of the results + - in: query + name: page_size + schema: + type: integer + example: 1 + default: 20 + required: false + description: Page size of the results + responses: + '200': + description: OK. + content: + application/json: + schema: + type: object + properties: + page_number: + type: integer + example: 1 + page_size: + type: integer + example: 10 + _embedded: + type: "array" + items: + $ref: '#/components/schemas/GetAllAccountResponse' + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta/chatapp-accounts?page_number=1&page_size=10" + next: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta/chatapp-accounts?page_number=3&page_size=10" + prev: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta/chatapp-accounts?page_number=1&page_size=10" + first: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta/chatapp-accounts?page_number=1&page_size=10" + last: + type: object + properties: + href: + type: string + example: "https://api.nexmo.com/beta/chatapp-accounts?page_number=4&page_size=10" + '401': + description: Unauthorized. + content: + application/json: + schema: + $ref: '#/components/schemas/401Response' + /{provider}/{external_id}/applications: + post: + security: + - bearerAuth: [] + - basicAuth: [] + summary: Link application to an account + operationId: LinkApplication + tags: + - Application + parameters: + - in: path + name: provider + schema: + type: string + enum: + - messenger + - viber_service_msg + - whatsapp + required: true + description: Provider of the account you want to assign an application to + - in: path + name: external_id + schema: + type: string + required: true + description: External id of the account you want to assign an application to. This is channel dependent. For Facebook it will be your Facebook Page ID, for Viber your Viber Service Message ID and for WhatsApp your WhatsApp number. + requestBody: + description: Request body can contain any of the following. Please note, the only one application can be linked to the account. + required: true + content: + application/json: + schema: + type: object + required: + - application + properties: + application: + type: string + example: "applicationId" + description: There is just one application allowed per an account. The application type must be type "messages". For more information please see [Application API Spec](https://developer.nexmo.com/api/application.v2) + responses: + '200': + description: OK. + content: + application/json: + schema: + $ref: '#/components/schemas/AccountResponse' + '401': + description: Unauthorized. + content: + application/json: + schema: + $ref: '#/components/schemas/401Response' + '403': + description: Forbidden. + content: + application/json: + schema: + type: object + properties: + type: + type: string + example: "https://www.nexmo.com/messages/Errors#Forbidden" + title: + type: string + example: "Forbidden" + detail: + type: string + example: "Cannot link application" + description: The application does not exist or the application type is not "messages" + '409': + description: Conflict. + content: + application/json: + schema: + type: object + properties: + type: + type: string + example: "https://www.nexmo.com/messages/Errors#Conflict" + title: + type: string + example: "Application conflict" + detail: + type: string + example: "Unable to link application" + description: The account reached limit of maximum number of linked applications. (The only one application is allowed per an account) + /{provider}/{external_id}/applications/{application_id}: + delete: + security: + - bearerAuth: [] + - basicAuth: [] + summary: Unlink application from an account + operationId: UnliWithoutApplicationnkApplication + tags: + - Application + parameters: + - in: path + name: provider + schema: + type: string + enum: + - messenger + - viber_service_msg + - whatsapp + required: true + description: Provider of the account you want to unlink an application from + - in: path + name: external_id + schema: + type: string + required: true + description: External id of the account you want to unlink an application from + - in: path + name: application_id + schema: + type: string + required: true + description: Id of the application you want to unlink + responses: + '204': + description: No Content. + '401': + description: Unauthorized. + content: + application/json: + schema: + $ref: '#/components/schemas/401Response' + '403': + description: Forbidden. + content: + application/json: + schema: + type: object + properties: + type: + type: string + example: "https://www.nexmo.com/messages/Errors#Forbidden" + title: + type: string + example: "Forbidden" + detail: + type: string + example: "Cannot unlink application" + '409': + description: Conflict. + content: + application/json: + schema: + type: object + properties: + type: + type: string + example: "https://www.nexmo.com/messages/Errors#Conflict" + title: + type: string + example: "Application conflict" + detail: + type: string + example: "Unable to unlink application" + description: The application is not linked or doesn't exist. + +components: + + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + basicAuth: + type: http + scheme: basic + + schemas: + AccountResponse: + type: object + required: + - external_id + - api_key + - provider + properties: + name: + type: string + example: "name" #optional + description: The account name + applications: + type: "array" + items: + type: string + example: ["applicationId"] + description: The array of associated application ids + external_id: + type: string + example: "12345678" + description: The external identifier for this account + api_key: + type: string + example: "abcd1234" + description: The external api key for this account + provider: + type: string + enum: + - messenger + - viber_service_msg + - whatsapp + example: "messenger" + description: The provider (will be one of `messenger, viber_service_msg, whatsapp`). + access_token: + type: string + example: "accessToken" #optional + description: The provider access token (only for `messenger`) + + MessengerAccountResponse: + type: object + required: + - external_id + - api_key + - provider + - access_token + properties: + name: + type: string + example: "optionalName" + description: The account name + applications: + type: "array" + items: + type: string + example: ["optionalApplicationId"] + description: The array of associated application ids + external_id: + type: string + example: "12345678" + description: The external identifier for this account + api_key: + type: string + example: "abcd1234" + description: The external api key for this account + provider: + type: string + example: "messenger" + description: The provider (will be `messenger`). + access_token: + type: string + example: "myAccessToken" + description: The provider access token + + VSMAccountResponse: + type: object + required: + - external_id + - api_key + - provider + properties: + name: + type: string + example: "optionalName" + description: The account name + applications: + type: "array" + items: + type: string + example: ["optionalApplicationId"] + description: The array of associated application ids + external_id: + type: string + example: "12345678" + description: The external identifier for this account + api_key: + type: string + example: "abcd1234" + description: The external api key for this account + provider: + type: string + example: "viber_service_msg" + description: The provider (will be `viber_service_msg`). + + WAAccountResponse: + type: object + required: + - external_id + - api_key + - provider + properties: + name: + type: string + example: "optionalName" + description: The account name + applications: + type: "array" + items: + type: string + example: ["optionalApplicationId"] + description: The array of associated application ids + external_id: + type: string + example: "12345678" + description: The external identifier for this account + api_key: + type: string + example: "abcd1234" + description: The external api key for this account + provider: + type: string + example: whatsapp + description: The provider (will be `whatsapp`). + + GetAllAccountResponse: + allOf: + - $ref: '#/components/schemas/MessengerAccountResponse' + - $ref: '#/components/schemas/VSMAccountResponse' + - $ref: '#/components/schemas/WAAccountResponse' + + 401Response: + type: object + properties: + title: + type: string + example: "Unauthorised" + detail: + type: string + example: "Request header 'Authorization' missing / Invalid Token" + 403Response: + type: object + properties: + title: + type: string + example: "Wrong authentication - You are not authorised to access this resource" +tags: + - name: "Application" + description: Inbound messages to an external account which is linked to an application will be delivered to the application's inbound URL. + - name: "Account" + description: An external-account used as the `from` field in the Messages API and Dispatch API + - name: "Facebook Messenger" + description: 'Managing your [Facebook Messenger](https://developer.nexmo.com/messages/concepts/facebook) account' + - name: "Viber Service Message" + description: 'Managing your [Viber Service Message](https://developer.nexmo.com/messages/concepts/viber) account' + - name: "Whatsapp" + description: 'Managing your [Whatsapp](https://developer.nexmo.com/messages/concepts/whatsapp) account' diff --git a/tests/resources/definitions/media.yml b/tests/resources/definitions/media.yml new file mode 100755 index 00000000..ad5d5505 --- /dev/null +++ b/tests/resources/definitions/media.yml @@ -0,0 +1,235 @@ +openapi: "3.0.0" +info: + version: 1.0.1 + title: Media API + description: The Media API can be used to query, download and delete media items such as audio files for use with other Nexmo APIs. + x-label: 'BETA' + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' +servers: + - url: https://api.nexmo.com/v3/media +paths: + /: + get: + summary: List and search media items + operationId: list-and-search-media-items + description: Retrieve information about multiple media items with the ability to search and paginate. + parameters: + - name: order + description: The order of search results. + in: query + schema: + type: string + example: ascending + enum: + - ascending + - descending + default: descending + - name: page_index + description: Which page to retrieve in pagination + in: query + schema: + type: integer + example: 1 + default: 0 + - name: page_size + description: How many items at most per page + in: query + schema: + type: integer + example: 50 + default: 20 + - name: start_time + description: Retrieve results created on or after this timestap. + in: query + schema: + type: string + example: '2020-01-01T14:00:00.000Z' + default: '1 week ago' + x-default-is-meta: true + - name: end_time + description: Retrieve results created on or before this timestamp. + in: query + schema: + type: string + example: '2020-01-01T14:00:00.000Z' + responses: + '200': + description: Successfully retrieved + content: + application/json: + schema: + type: object + properties: + page_size: + type: integer + description: The amount of records returned in this response. + default: 0 + example: 20 + page_index: + type: integer + description: The `page_index` used in your request. + default: 0 + example: 0 + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + default: '' + example: "/v3/media?page_size=20&account_id=abcd1234&order=descending" + first: + type: object + properties: + href: + type: string + default: '' + example: "/v3/media?page_size=20&account_id=abcd1234&order=descending" + last: + type: object + properties: + href: + type: string + default: '' + example: "/v3/media?page_size=20&account_id=abcd1234&order=descending" + count: + type: integer + description: The total number of records returned by your request. + default: 0 + example: 1 + _embedded: + type: object + description: >- + A collection of media items. + See [retrieve a media item](#retrieve-a-media-item) for a + description of the returned fields + properties: + media: + type: array + items: + $ref: "#/components/schemas/Media" + + /:id/info: + get: + summary: Retrieve a media item + operationId: retrieve-a-media-item + description: Retrieve information about a single media item + responses: + '200': + description: Successfully retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/Media" + put: + summary: Update a media item + operationId: update-a-media-item + description: Update a previously created media item by ID. + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + public: + description: Whether the item is publicly available without authentication. + type: boolean + example: true + metadata_primary: + description: A string containing metadata about the media file. + type: string + example: 'foo,bar' + metadata_secondary: + description: A string containing further metadata about the media file. + type: string + example: '123' + title: + description: A string containing a title for the media file. + type: string + example: 'Very important recording' + description: + description: A description of the media file. + type: string + example: 'This is a very important recording. Do not delete.' + mime_type: + description: The MIME type of the media file. + type: string + example: 'audio/vnd.wave' + max_downloads_allowed: + description: The maximum number of times the file may be downloaded. Unlimited when not provided. + type: integer + example: 100 + responses: + '204': + description: Successfully updated + delete: + summary: Delete a media item + operationId: delete-a-media-item + description: Delete a previously created media item by ID. + responses: + '204': + description: Successfully deleted +components: + schemas: + Media: + type: object + properties: + id: + type: string + description: "A UUID representing the object." + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + original_file_name: + type: string + description: "The filename of the object as it was originally uploaded." + example: "test.wav" + mime_type: + type: string + description: "The IETF MIME type of the file." + example: "audio/vnd.wave" + account_id: + type: string + description: "The ID of your Nexmo account. This is the same as your API key." + example: abcd1234 + store_id: + type: string + description: "An internal identifier of how the file is stored." + example: s3 + max_downloads_allowed: + type: integer + description: "The maximum number of times the file may be downloaded." + example: 0 + times_downloaded: + type: integer + description: "The number of times the file has been downloaded." + example: 1 + etag: + type: string + description: "An identifier for the content. This will change if the content of the file has been changed (i.e. if you upload a new version of the file). For more information see Wikipedia: [HTTP ETag](https://en.wikipedia.org/wiki/HTTP_ETag)" + example: aaaaaaaabbbbccccdddd0123456789ab + media_size: + type: integer + description: "The size of the file in bytes" + example: 1234567 + time_created: + type: string + description: "A timestamp for the time that the file was created" + example: "2020-01-01T14:00:00.000Z" + time_last_updated: + type: string + description: "A timestamp for the time that the file was last modified" + example: "2020-01-01T14:00:00.000Z" + public: + type: boolean + description: "Whether the item is available for download without authentication." + example: false + metadata_primary: + type: string + description: A user set string containing metadata about the media file. + metadata_secondary: + type: string + description: A user set string containing further metadata about the media file. diff --git a/tests/resources/definitions/messages-olympus.yml b/tests/resources/definitions/messages-olympus.yml new file mode 100755 index 00000000..256faf08 --- /dev/null +++ b/tests/resources/definitions/messages-olympus.yml @@ -0,0 +1,745 @@ +openapi: "3.0.0" +info: + version: 0.3.6 + title: Messages API + description: 'The Messaging API is a new API that consolidates all messaging channels. It encapsulates a user (developer) from having to use multiple APIs to interact with our various channels such as SMS, MMS, Viber, Facebook Messenger, etc. The API normalises information across all channels to abstracted to, from and content. This API is currently in Beta.' + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' + x-label: Beta +servers: + - url: https://api.nexmo.com/v0.1/messages +paths: + /: + post: + security: + - bearerAuth: [] + - basicAuth: [] + summary: Send a Message + operationId: NewMessage + requestBody: + description: Send a Message. + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/NewMessage' + responses: + '202': + description: Accepted. + content: + application/json: + schema: + $ref: '#/components/schemas/Response' + '400': + description: Bad Request. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + callbacks: + message-status: + '{$request.body#/callback}': + post: + summary: Message Status + operationId: message-status + x-example-path: '/webhooks/message-status' + description: 'Webhooks to inform about events happening to the message at communication level (has it been delivered, rejected by the provider...).' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MessageStatus' + responses: + '200': + description: Your server returns this code if it accepts the callback. + inbound-message: + '{$request.body#/callback}': + post: + summary: Inbound Message + operationId: inbound-message + x-example-path: '/webhooks/inbound-message' + description: An inbound message from a customer to you. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/InboundMessage' + responses: + '200': + description: Your server returns this code if it accepts the callback. + + +components: + + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + basicAuth: + type: http + scheme: basic + + schemas: + + NewMessage: + required: + - to + - from + - message + properties: + to: + $ref: '#/components/schemas/ToProperty' + from: + $ref: '#/components/schemas/FromProperty' + message: + $ref: '#/components/schemas/MessageProperty' + client_ref: + type: string + description: Client reference of up to 40 characters. The reference will be present in every message status. + example: 'my-personal-reference' + + Response: + required: + - message_uuid + properties: + message_uuid: + type: string + description: The UUID of the message. + example: 'aaaaaaaa-bbbb-cccc-dddd-0123456789ab' + Error: + description: 'The error format is standardized to the 4xx/5xx range with a code and a human readable explanation.' + required: + - type + - title + - detail + - instance + properties: + type: + type: string + example: 'https://www.nexmo.com/messages/Errors#InvalidParams' + title: + type: string + example: 'Invalid Parameters' + detail: + type: string + example: 'Your request parameters did not validate.' + instance: + type: string + example: f94b4e56604e07e5e5ad5a7228618f81 + + MessageStatus: + type: object + required: + - message_uuid + - to + - from + - timestamp + - status + properties: + message_uuid: + type: string + description: The UUID of the message. + example: 'aaaaaaaa-bbbb-cccc-dddd-0123456789ab' + to: + $ref: '#/components/schemas/ToProperty' + from: + $ref: '#/components/schemas/FromProperty' + timestamp: + $ref: '#/components/schemas/TimestampProperty' + status: + type: string + example: delivered + description: The status of the message. The `read` message status is available for `messenger`, `whatsapp` and `viber`. + enum: + - submitted + - delivered + - read + - rejected + - undeliverable + error: + type: object + properties: + code: + type: integer + example: 1300 + description: The error code. See [our errors list](https://developer.nexmo.com/api-errors/messages-olympus) for a list of possible errors + reason: + type: string + example: 'Not part of the provider network' + description: Text describing the error. See [our errors list](https://developer.nexmo.com/api-errors/messages-olympus) for a list of possible errors + usage: + type: object + properties: + currency: + type: string + example: EUR + description: The charge currency in ISO 4217 format. + enum: + - EUR + price: + type: string + example: '0.0333' + description: The charge amount as a stringified number. + client_ref: + type: string + description: The client's reference. + example: 'my-personal-reference' + + InboundMessage: + type: object + required: + - message_uuid + - to + - from + - timestamp + properties: + message_uuid: + type: string + example: 'aaaaaaaa-bbbb-cccc-dddd-0123456789ab' + description: The UUID of the message. + to: + type: object + required: + - type + properties: + type: + type: string + description: The type of message being received. + example: messenger + enum: + - messenger + - mms + - whatsapp + - viber_service_msg + id: + type: string + example: '01234567' + description: | + **Messenger** or **Viber**: + The ID of the recipient. + number: + type: string + minLength: 1 + maxLength: 50 + example: '447700900000' + description: | + **WhatsApp** or **MMS**: + The phone number of the message recipient in the [E.164](https://en.wikipedia.org/wiki/E.164) format. + from: + type: object + required: + - type + properties: + type: + type: string + description: The type of message being sent. + example: messenger + enum: + - messenger + - mms + - whatsapp + - viber_service_msg + id: + type: string + description: The ID of the sender. + example: '0123456789012345' + number: + type: string + minLength: 1 + maxLength: 50 + example: '447700900000' + description: | + **WhatsApp** or **MMS** or **Viber**: + The phone number of the message sender in the [E.164](https://en.wikipedia.org/wiki/E.164) format. + timestamp: + type: string + format: ISO 8601 + description: The datetime of when the event occurred. + example: '2020-01-01T14:00:00.000Z' + message: + type: object + properties: + content: + type: object + properties: + type: + type: string + description: | + The type of message being received. + **whatsapp** and **messenger** supports `text`, `image`, `audio`, `video`, `file` and `location`. WhatsApp maximum inbound size is 64mb. + **mms** supports `image`. + **viber** supports `text`. + example: 'text' + enum: + - text + - image + - audio + - video + - file + - location + text: + type: string + description: The body of the message. + example: 'Hello World!' + image: + $ref: '#/components/schemas/ImageProperty' + audio: + $ref: '#/components/schemas/AudioProperty' + video: + $ref: '#/components/schemas/VideoPropertyInbound' + file: + $ref: '#/components/schemas/FileProperty' + location: + $ref: '#/components/schemas/LocationProperty' + + ToProperty: + type: object + required: + - type + properties: + type: + type: string + description: The type of message that you want to send. + example: 'sms' + enum: + - sms + - viber_service_msg + - messenger + - whatsapp + - mms + id: + description: | + **Messenger**: The ID of the message recipient. This value should be the `from.id` value you received in the inbound messenger event. + type: string + minLength: 1 + maxLength: 50 + example: '0123456789012345' + number: + type: string + minLength: 1 + maxLength: 50 + example: '447700900000' + description: | + **SMS**, **Viber**, **WhatsApp** or **MMS**: + The phone number of the message recipient in the [E.164](https://en.wikipedia.org/wiki/E.164) format. Don't use a leading + or 00 when entering a phone number, start with the country code, for example, 447700900000. + FromProperty: + type: object + required: + - type + properties: + type: + type: string + description: The type of message that you want to send. + example: sms + enum: + - sms + - viber_service_msg + - messenger + - whatsapp + - mms + id: + description: | + Your ID for the platform that you are sending from. + + **Messenger**: This value should be the `to.id` value you received in the inbound messenger event. + + **Viber**: This is your Service Message ID given to you by Nexmo Account Manager. To find out more please visit [nexmo.com/products/messages](https://www.nexmo.com/products/messages). + + type: string + minLength: 1 + maxLength: 50 + example: '0123456789012345' + number: + type: string + minLength: 1 + maxLength: 50 + example: '447700900000' + description: | + **SMS**: The phone number of the message recipient in the [E.164](https://en.wikipedia.org/wiki/E.164) format. Don't use a leading + or 00 when entering a phone number, start with the country code, for example, 447700900000. + + **WhatsApp**: This is your WhatsApp Business Number given to you by Nexmo Account Manager. To find out more please visit [nexmo.com/products/messages](https://www.nexmo.com/products/messages). + + **MMS**: US shortcode + MessageProperty: + type: object + required: + - content + properties: + content: + type: object + properties: + type: + description: | + The type of message that you are sending. + + **Messenger**: supports `text`, `image`, `audio`, `video` and `file`. + + **Viber Service Messages**: supports `image`, `text` and `custom`. + + **WhatsApp**: supports `template`, `text`, `image`, `audio`, `file`, `video` and `custom`. Maximum outbound media size is 64mb. + + **SMS**: supports `text`. + + **MMS**: supports `image`. + type: string + enum: + - text + - image + - audio + - video + - file + - template + - custom + example: 'text' + text: + description: | + The text of the message. + + **Messenger**: is limited to 640 characters, including unicode. + + **SMS**: is limited to 1000 characters. The Messages API automatically detects unicode characters when sending SMS and sends the message as a unicode SMS. For more information on how concatenation and encoding please visit: [developer.nexmo.com/messaging/sms/guides/concatenation-and-encoding](https://developer.nexmo.com/messaging/sms/guides/concatenation-and-encoding). + + **Viber**: is limited to 1000 characters, including unicode. + + **WhatsApp**: is limited to 4096 characters, including unicode. + type: string + minLength: 1 + maxLength: 4096 + example: 'Nexmo Verification code: 64873. Valid for 10 minutes.' + image: + $ref: '#/components/schemas/ImageProperty' + audio: + $ref: '#/components/schemas/AudioProperty' + video: + $ref: '#/components/schemas/VideoProperty' + file: + $ref: '#/components/schemas/FileProperty' + template: + $ref: '#/components/schemas/TemplateProperty' + # @TODO: Clarify requirements + # custom: + # type: object + # description: This is a highly experimental feature. To enable the user to send any type of Messenger message we have included a custom object. To use this feature include the original Messenger API payload from the message object onwards. + # properties: + # custom: + # type: object + viber_service_msg: + type: object + properties: + category: + description: 'The use of different category tags enables the business to send messages for different use cases. For Viber Service Messages the first message sent from a business to a user must be personal, informative & a targeted message - not promotional. By default Nexmo sends the `transaction` category to Viber Service Messages.' + type: string + example: 'transaction' + enum: + - transaction + - promotion + ttl: + description: 'Set the time-to-live of message to be delivered in seconds. i.e. if the message is not delivered in 600 seconds then delete the message.' + type: integer + example: 600 + minimum: 30 + maximum: 259200 + type: + description: 'Viber-specific type definition. To use "template", please contact Nexmo Account Manager to setup your templates. To find out more please visit [nexmo.com/products/messages](https://www.nexmo.com/products/messages).' + type: string + example: 'template' + messenger: + type: object + properties: + category: + description: 'The use of different category tags enables the business to send messages for different use cases. For Facebook Messenger they need to comply with their [Messaging Types policy]( https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types). Nexmo maps our `category` to their `messaging_type`. If `message_tag` is used, then an additional `tag` for that type is mandatory. By default Nexmo sends the `response` category to Facebook Messenger.' + type: string + example: 'message_tag' + enum: + - response + - update + - message_tag + tag: + description: ‘A full list of the possible tags is available on [developers.facebook.com](https://developers.facebook.com/docs/messenger-platform/send-messages/message-tags)' + type: string + example: 'ticket_update' + whatsapp: + type: object + properties: + policy: + description: 'Please note that WhatsApp will deprecate `fallback` policy in January 2020. There are two policies that you can specify when sending a Message Template: `deterministic` and `fallback`. `deterministic` — Deliver the Message Template in exactly the language and locale asked for. `fallback` — Deliver the Message Template in the language that matches users language/locale setting on device. If one can not be found, deliver using the specified fallback language.' + type: string + example: 'deterministic' + enum: + - fallback + - deterministic + locale: + description: 'We are using the industry standard, BCP 47, for locales. So in your API call to the /messages API you will need to put “en-GB” and this will refer to the “en_GB” template for WhatsApp. A full list of the possible locales is available on [developers.facebook.com](https://developers.facebook.com/docs/whatsapp/message-templates/creation#translations).' + type: string + example: 'en-GB' + + + TimestampProperty: + type: string + format: ISO 8601 + description: The datetime of when the event occurred. + example: '2020-01-01T14:00:00.000Z' + + ImageProperty: + type: object + properties: + url: + type: string + description: | + The URL of the image attachment. + + **messenger** and **mms** supports .jpg, .jpeg, .png and .gif. + + **whatsapp** and **viber_service_msg** supports .jpg .jpeg, and .png. + minLength: 10 + maxLength: 2000 + example: 'https://example.com/image.jpg' + caption: + type: string + description: 'Additional text to accompany the image. Supported by WhatsApp and MMS.' + minLength: 1 + maxLength: 3000 + example: 'Additional text to accompany the image.' + AudioProperty: + type: object + properties: + url: + type: string + description: | + The URL of the audio attachment. + + **messenger** supports .mp3. + + **whatsapp** supports .aac, .m4a, .amr, .mp3 and .opus. + minLength: 10 + maxLength: 2000 + example: 'https://example.com/audio.mp3' + VideoProperty: + type: object + properties: + url: + type: string + description: | + The URL of the video attachment. + + **messenger** supports .mp4 + + **whatsapp** supports .mp4 and .3gpp. Note, only H.264 video codec and AAC audio codec is supported. + minLength: 10 + maxLength: 2000 + example: 'https://example.com/video.mp4' + VideoPropertyInbound: + type: object + properties: + url: + type: string + description: | + The URL of the video attachment. + + **messenger** and **whatsapp** supports .mp4. + minLength: 10 + maxLength: 2000 + example: 'https://example.com/video.mp4' + caption: + type: string + description: 'Additional text to accompany the image. Only supported by WhatsApp. Only present if specified.' + minLength: 1 + maxLength: 3000 + example: 'Additional text to accompany the image.' + FileProperty: + type: object + properties: + url: + type: string + description: | + The URL of the file attachment. + + **messenger** supports a wide range of attachments including .zip, .csv and .pdf. + + **whatsapp** supports .pdf, .doc(x), .ppt(x) and .xls(x).' + minLength: 10 + maxLength: 2000 + example: 'https://example.com/file.zip' + caption: + type: string + description: 'Additional text to accompany the image. Only supported by WhatsApp. Optional. Only present if specified.' + minLength: 1 + maxLength: 3000 + example: 'Additional text to accompany the image.' + TemplateProperty: + type: object + properties: + name: + type: string + description: 'The name of the template. For WhatsApp use your Whatsapp namespace (available via Facebook Business Manager), followed by a colon `:` and the name of the template to use.' + example: 'WhatsApp_namespace:template_name' + parameters: + type: array + items: + type: object + properties: + default: + type: string + description: 'The parameters are an array. The first value being {{1}} in the template.' + example: '1234' + + LocationProperty: + type: object + properties: + lat: + type: string + description: The latitude of the location attachment. + example: '51.5228349' + long: + type: string + description: The longitude of the location attachment. + example: '-0.0854414' + url: + type: string + description: Depending on the provider, this can either be the location on a map or the website of the business at this location. + address: + type: string + description: The address of the location attachment. + example: '15 Bonhill St London EC2A 4DN' + name: + type: string + description: The name of the location attachment. + example: 'Nexmo London' + +x-errors: + "1000": + description: Throttled - You have exceeded the submission capacity allowed on this account. Please wait and retry + + "1010": + description: Missing params - Your request is incomplete and missing some mandatory parameters. + + "1020": + description: Invalid params - The value of one or more parameters is invalid. + + "1021": + description: Invalid tag - The tag value is invalid. + + "1022": + description: Invalid template - Invalid template or template parameters + + "1030": + description: Internal error - There was an error processing your request in the Platform. + + "1040": + description: Invalid message - The Platform was unable to process your request. For example, due to an unrecognised prefix for the phone number. + + "1050": + description: Number barred - The number you are trying to submit to is blacklisted and may not receive messages. + + "1060": + description: Partner account barred - The `api_key` you supplied is for an account that has been barred from submitting messages. + + "1070": + description: Partner quota exceeded - Your pre-paid account does not have sufficient credit to process this message. + + "1080": + description: Account not enabled for REST - This account is not provisioned for REST submission, you should use SMPP on the SMS API. + + "1090": + description: Message too long - The length of `udh` and `body` was greater than 140 octets for a binary type SMS request. + + "1100": + description: Communication Failed - Message was not submitted because there was a communication failure. + + "1120": + description: Illegal Sender Address - rejected - Due to local regulations, the `SenderID` you set in from in the request was not accepted. Please check the Global messaging section. + + "1130": + description: Invalid TTL - The value of `ttl` in your request was invalid. + + "1140": + description: Facility not allowed - Your request makes use of a facility that is not enabled on your account. + + "1150": + description: Invalid Message class - The value of `message-`class in your request was out of range. See https://en.wikipedia.org/wiki/Data_Coding_Scheme. + + "1160": + description: Non White-listed Destination - The phone number you set in to is not in your pre-approved destination list. To send messages to this phone number, add it using Dashboard. + + "1170": + description: Invalid or Missing Msisdn Param - The phone number you supplied in the to parameter of your request was either missing or invalid. + + "1180": + description: Absent Subscriber Temporary - This message was not delivered because to was temporarily unavailable. For example, the handset used for to was out of coverage or switched off. This is a temporary failure, retry later for a positive result. + + "1190": + description: Absent Subscriber Permanent - `to` is no longer active, You should remove this phone number from your database. + + "1200": + description: Portability Error - There is an issue after the user has changed carrier for to. If the user wants to receive messages from you, they need to contact their carrier directly. + + "1210": + description: Anti-Spam Rejection - Carriers often apply restrictions that block messages following different criteria. For example on SenderID or message content. + + "1220": + description: Handset Busy - The handset associated with to was not available when this message was sent. If status is rejected, this is a temporary failure; retry later for a positive result. If status is submitted, this message has is in the retry scheme and will be resent until it expires in 24-48 hours. + + "1230": + description: Network Error - A network failure while sending your message. This is a temporary failure, retry later for a positive result. + + "1240": + description: Illegal Number - You tried to send a message to a blacklisted phone number. That is, the user has already sent a STOP opt-out message and no longer wishes to receive messages from you. + + "1241": + description: Too many send requests - Too many send requests to phone numbers. + + "1250": + description: Unroutable - The chosen route to send your message is not available. This is because the phone number is either currently on an unsupported network or on a pre-paid or reseller account that could not receive a message sent by from. To resolve this issue either email us at support@nexmo.com or create a helpdesk ticket at https://help.nexmo.com. + + "1260": + description: Destination unreachable - The message could not be delivered to the phone number. If using Viber Service Messages your account might not be enabled for this country. + + "1270": + description: Subscriber Age Restriction - The carrier blocked this message because the content is not suitable for to based on age restrictions. + + "1280": + description: Number Blocked by Carrier - The carrier blocked this message. This could be due to several reasons. For example, to's plan does not include SMS or the account is suspended. + + "1290": + description: Pre-Paid - Insufficient funds - to’s pre-paid account does not have enough credit to receive the message. + + "1300": + description: Not part of the provider network - The number or ID is not a user in the provider network. + + "1310": + description: Not suitable device - The user's device can't receive the message. + + "1320": + description: Message already sent - The message was already sent. + + "1330": + description: Unknown - An unknown error was received from the carrier who tried to send this this message. Depending on the carrier, that to is unknown. When you see this error, and status is rejected, always check if to in your request was valid. + + "1331": + description: Provider error - The provider is not responding or unable to process the request. Please try sending your message in a few minutes time. + + "1340": + description: Outside of the allowed window - This message is sent outside of allowed response window. + + "1350": + description: Phone matching fee not paid - Requires phone matching access fee to be paid by the Facebook Page. + + "1360": + description: TTL was activated - TTL was activated, no callbacks and no charge will be issued. + + "1370": + description: Expired access Token - Please reauthenticate your Facebook Page with Nexmo. + + "1380": + description: Invalid resource - Please check that the URL your provided to your resrouce is accesible and valid. + + "1381": + description: Resource size is too large - Please try sending a smaller media file. + + "1382": + description: Resource type is invalid - Please check that the file you are trying to send is valid. diff --git a/tests/resources/definitions/number-insight.yml b/tests/resources/definitions/number-insight.yml new file mode 100755 index 00000000..6273ed19 --- /dev/null +++ b/tests/resources/definitions/number-insight.yml @@ -0,0 +1,1115 @@ +--- +openapi: 3.0.0 +servers: + - url: "https://api.nexmo.com/ni" +info: + title: Number Insight API + version: 1.0.6 + description: >- + Nexmo's Number Insight API delivers real-time intelligence about the validity, reachability and roaming status of a phone number and tells you how to format the number correctly in your application. There are three levels of Number Insight API available: [Basic, Standard and Advanced](https://developer.nexmo.com/number-insight/overview#basic-standard-and-advanced-apis). The advanced API is available asynchronously as well as synchronously. + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: "https://developer.nexmo.com/" + termsOfService: "https://www.nexmo.com/terms-of-use" + license: + name: "The MIT License (MIT)" + url: "https://opensource.org/licenses/MIT" +externalDocs: + url: https://developer.nexmo.com/api/number-insight + x-sha1: 081f6d985e2e4a75586da1654fde880a96885405 +security: + - apiKey: [] + apiSecret: [] +paths: + "/basic/{format}": + parameters: + - $ref: "#/components/parameters/format" + get: + operationId: getNumberInsightBasic + summary: Basic Number Insight + description: | + Provides [basic number insight](/number-insight/overview#basic-standard-and-advanced-apis) information about a number. + + Note that this endpoint also supports `POST` requests. + parameters: + - $ref: "#/components/parameters/number" + - $ref: "#/components/parameters/country" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/niResponseJsonBasic" + text/xml: + schema: + $ref: "#/components/schemas/niResponseXmlBasic" + "/standard/{format}": + parameters: + - $ref: "#/components/parameters/format" + get: + operationId: getNumberInsightStandard + summary: Standard Number Insight + description: | + Provides [standard number insight](/number-insight/overview#basic-standard-and-advanced-apis) information about a number. + + Note that this endpoint also supports `POST` requests. + parameters: + - $ref: "#/components/parameters/number" + - $ref: "#/components/parameters/country" + - $ref: "#/components/parameters/cnam" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/niResponseJsonStandard" + text/xml: + schema: + $ref: "#/components/schemas/niResponseXmlStandard" + "/advanced/async/{format}": + parameters: + - $ref: "#/components/parameters/format" + get: + operationId: getNumberInsightAsync + summary: Advanced Number Insight (async) + description: | + Provides [advanced number insight](/number-insight/overview#basic-standard-and-advanced-apis) number information **asynchronously** using the URL specified in the `callback` parameter. Nexmo recommends asynchronous use of the Number Insight Advanced API, to avoid timeouts. + + Note that this endpoint also supports `POST` requests. + parameters: + - $ref: "#/components/parameters/callback" + - $ref: "#/components/parameters/number" + - $ref: "#/components/parameters/country" + - $ref: "#/components/parameters/cnam" + - $ref: "#/components/parameters/ip" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/niResponseAsync" + text/xml: + schema: + $ref: "#/components/schemas/niResponseAsync" + callbacks: + onData: + "{$request.query.callback}": + post: + operationId: asyncCallback + summary: Asynchronous response + description: Contains the response to your Number Insight Advanced API request. + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/niResponseJsonAdvanced" + text/xml: + schema: + $ref: "#/components/schemas/niResponseXmlAdvanced" + responses: + "200": + description: OK + "/advanced/{format}": + parameters: + - $ref: "#/components/parameters/format" + get: + operationId: getNumberInsightAdvanced + summary: Advanced Number Insight (sync) + description: | + Provides [advanced number insight](/number-insight/overview#basic-standard-and-advanced-apis) information about a number synchronously, in the same way that the basic and standard endpoints do. + + Nexmo recommends accessing the Advanced API **asynchronously** using the `/advanced/async` endpoint, to avoid timeouts. + + Note that this endpoint also supports `POST` requests. + parameters: + - $ref: "#/components/parameters/number" + - $ref: "#/components/parameters/country" + - $ref: "#/components/parameters/cnam" + - $ref: "#/components/parameters/ip" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/niResponseJsonAdvanced" + text/xml: + schema: + $ref: "#/components/schemas/niResponseXmlAdvanced" +components: + parameters: + format: + name: format + in: path + required: true + description: "The format of the response" + example: json + schema: + type: string + enum: + - "json" + - "xml" + number: + name: number + in: query + description: "A single phone number that you need insight about in national or international format." + example: "447700900000" + required: true + schema: + type: string + pattern: '^[0-9-+\(\)\s]*$' + country: + name: country + in: query + example: "GB" + description: "If a number does not have a country code or is uncertain, set the two-character country code. This code must be in ISO 3166-1 alpha-2 format and in upper case. For example, GB or US. If you set country and number is already in [E.164](https://en.wikipedia.org/wiki/E.164) format, country must match the country code in number." + schema: + type: string + pattern: "[A-Z]{2}" + cnam: + name: cnam + in: query + example: true + description: "Indicates if the name of the person who owns the phone number should be looked up and returned in the response. Set to true to receive phone number owner name in the response. This features is available for US numbers only and incurs an additional charge." + schema: + type: boolean + default: false + ip: + name: ip + in: query + example: "123.0.0.255" + description: "This parameter is deprecated as we are no longer able to retrieve reliable IP data globally from carriers. " + schema: + type: string + deprecated: true + callback: + name: callback + in: query + example: "https://example.com/callback" + description: "The callback URL" + required: true + schema: + type: string + format: uriref + + schemas: + niResponseAsync: + type: object + xml: + name: lookup + properties: + request_id: + type: string + description: "The unique identifier for your request. This is a alphanumeric string up to 40 characters." + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + maxLength: 40 + xml: + name: requestId + number: + type: string + description: "The `number` in your request" + example: "447700900000" + remaining_balance: + type: string + description: "Your account balance in EUR after this request. Not returned with Number Insight Advanced Async API." + example: "1.23456789" + xml: + name: remainingBalance + request_price: + type: string + description: "If there is an internal lookup error, the `refund_price` will reflect the lookup price. If `cnam` is requested for a non-US number the `refund_price` will reflect the `cnam` price. If both of these conditions occur, `refund_price` is the sum of the lookup price and `cnam` price." + example: "0.01500000" + xml: + name: requestPrice + status: + $ref: "#/components/schemas/niStandardAdvancedStatus" + + niResponseXmlBasic: + type: object + description: Basic + xml: + name: format + properties: + request_id: + type: string + description: "The unique identifier for your request. This is a alphanumeric string up to 40 characters." + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + maxLength: 40 + xml: + name: request_id + international_format_number: + type: string + description: "The `number` in your request in international format." + example: "447700900000" + local_number: + type: object + description: "An object containing the `number` in your request in the format used by the country the number belongs to." + properties: + country_code: + type: string + description: "Two character country code for `number`. This is in [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) format." + example: "GB" + pattern: "[A-Z]{2}" + xml: + attribute: true + country_code_iso3: + type: string + description: "Three character country code for `number`. This is in [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) format." + example: "GBR" + pattern: "[A-Z]{3}" + xml: + attribute: true + country_name: + type: string + description: "The full name of the country that `number` is registered in." + example: "United Kingdom" + xml: + attribute: true + country_prefix: + type: string + description: "The numeric prefix for the country that `number` is registered in." + example: "44" + xml: + attribute: true + number: + type: string + description: "The `number` in your request in the format used by the country the number belongs to." + example: "07700 900000" + xml: + x-text: true # see https://github.com/OAI/OpenAPI-Specification/issues/630 + error: + type: object + description: "The error code and status of your request" + properties: + code: + type: string + description: "The status code" + example: 0 + xml: + attribute: true + status_text: + type: string + description: "The status description of your request." + example: Success + xml: + x-text: true # see https://github.com/OAI/OpenAPI-Specification/issues/630 + + niResponseXmlStandard: + type: object + description: "Standard" + xml: + name: lookup + properties: + request_id: + type: string + description: "The unique identifier for your request. This is a alphanumeric string up to 40 characters." + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + maxLength: 40 + xml: + name: request_id + international_format_number: + type: string + description: "The `number` in your request in international format." + example: "447700900000" + local_number: + type: object + description: "An object containing the `number` in your request in the format used by the country the number belongs to." + properties: + country_code: + type: string + description: "Two character country code for `number`. This is in [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) format." + example: "GB" + pattern: "[A-Z]{2}" + xml: + attribute: true + country_code_iso3: + type: string + description: "Three character country code for `number`. This is in [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) format." + example: "GBR" + pattern: "[A-Z]{3}" + xml: + attribute: true + country_name: + type: string + description: "The full name of the country that `number` is registered in." + example: "United Kingdom" + xml: + attribute: true + country_prefix: + type: string + description: "The numeric prefix for the country that `number` is registered in." + example: "44" + xml: + attribute: true + number: + type: string + description: "The `number` in your request in the format used by the country the number belongs to." + example: "07700 900000" + xml: + x-text: true # see https://github.com/OAI/OpenAPI-Specification/issues/630 + error: + type: object + description: "The error code and status of your request" + properties: + code: + type: string + description: "The status code" + example: 0 + xml: + attribute: true + status_text: + type: string + description: "The status description of your request." + example: Success + xml: + x-text: true # see https://github.com/OAI/OpenAPI-Specification/issues/630 + request_price: + type: string + description: "If there is an internal lookup error, the `refund_price` will reflect the lookup price. If `cnam` is requested for a non-US number the `refund_price` will reflect the `cnam` price. If both of these conditions occur, `refund_price` is the sum of the lookup price and `cnam` price." + example: "0.01500000" + remaining_balance: + type: string + description: "Your account balance in EUR after this request. Not returned with Number Insight Advanced Async API." + example: "1.23456789" + current_carrier: + $ref: "#/components/schemas/niCurrentCarrierProperties" + original_carrier: + $ref: "#/components/schemas/niInitialCarrierProperties" + ported: + description: "If the user has changed carrier for number. The assumed status means that the information supplier has replied to the request but has not said explicitly that the number is ported" + properties: + ported_message: + type: string + description: "If the user has changed carrier for `number`. The assumed status means that the information supplier has replied to the request but has not said explicitly that the number is ported." + enum: + - unknown + - ported + - not_ported + - assumed_not_ported + - assumed_ported + example: "not_ported" + xml: + x-text: true + roaming: + type: object + description: "Information about the roaming status for number. This is applicable to mobile numbers only." + x-nexmo-developer-collection-description-shown: true + properties: + status: + type: string + enum: + - unknown + example: unknown + xml: + attribute: true + caller_identity: + type: object + x-nexmo-developer-collection-description-shown: true + description: "Contains details of the number owner, if `cnam` was set to `true` in the request." + properties: + caller-type: + type: string + description: "The value will be `business` if the owner of a phone number is a business. If the owner is an individual the value will be `consumer`. The value will be `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + enum: + - business + - consumer + - unknown + example: "consumer" + xml: + attribute: true + caller-name: + type: string + description: "Full name of the person or business who owns the phone number. `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John Smith" + xml: + attribute: true + first-name: + type: string + description: "First name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John" + xml: + attribute: true + last-name: + type: string + description: "Last name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "Smith" + xml: + attribute: true + caller_name: + type: string + description: "Full name of the person or business who owns the phone number. `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John Smith" + last_name: + type: string + description: "Last name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "Smith" + firs_name: + # Key is not a typo. Do not change. + type: string + description: "First name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John" + caller_type: + type: string + description: "The value will be `business` if the owner of a phone number is a business. If the owner is an individual the value will be `consumer`. The value will be `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + enum: + - business + - consumer + - unknown + example: "consumer" + + niResponseXmlAdvanced: + type: object + description: "Advanced" + xml: + name: lookup + properties: + request_id: + type: string + description: "The unique identifier for your request. This is a alphanumeric string up to 40 characters." + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + maxLength: 40 + xml: + name: request_id + international_format_number: + type: string + description: "The `number` in your request in international format." + example: "447700900000" + local_number: + type: object + x-nexmo-developer-collection-description-shown: true + description: "An object containing the `number` in your request in the format used by the country the number belongs to." + properties: + country_code: + type: string + description: "Two character country code for `number`. This is in [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) format." + example: "GB" + pattern: "[A-Z]{2}" + xml: + attribute: true + country_code_iso3: + type: string + description: "Three character country code for `number`. This is in [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) format." + example: "GBR" + pattern: "[A-Z]{3}" + xml: + attribute: true + country_name: + type: string + description: "The full name of the country that `number` is registered in." + example: "United Kingdom" + xml: + attribute: true + country_prefix: + type: string + description: "The numeric prefix for the country that `number` is registered in." + example: "44" + xml: + attribute: true + number: + type: string + description: "The `number` in your request in the format used by the country the number belongs to." + example: "07700 900000" + xml: + x-text: true # see https://github.com/OAI/OpenAPI-Specification/issues/630 + error: + type: object + x-nexmo-developer-collection-description-shown: true + description: "The error code and status of your request" + properties: + code: + type: string + description: "The status code" + example: 0 + xml: + attribute: true + status_text: + type: string + description: "The status description of your request." + example: Success + xml: + x-text: true # see https://github.com/OAI/OpenAPI-Specification/issues/630 + request_price: + type: string + description: "If there is an internal lookup error, the `refund_price` will reflect the lookup price. If `cnam` is requested for a non-US number the `refund_price` will reflect the `cnam` price. If both of these conditions occur, `refund_price` is the sum of the lookup price and `cnam` price." + example: "0.01500000" + remaining_balance: + type: string + description: "Your account balance in EUR after this request. Not returned with Number Insight Advanced Async API." + example: "1.23456789" + current_carrier: + $ref: "#/components/schemas/niCurrentCarrierProperties" + original_carrier: + $ref: "#/components/schemas/niInitialCarrierProperties" + ported: + description: "If the user has changed carrier for `number`. The assumed status means that the information supplier has replied to the request but has not said explicitly that the number is ported." + properties: + ported_message: + type: string + description: "If the user has changed carrier for `number`. The assumed status means that the information supplier has replied to the request but has not said explicitly that the number is ported." + enum: + - unknown + - ported + - not_ported + - assumed_not_ported + - assumed_ported + example: "not_ported" + xml: + x-text: true + caller_identity: + description: "Contains details of the number owner, if `cnam` was set to `true` in the request." + properties: + caller-type: + type: string + description: "The value will be `business` if the owner of a phone number is a business. If the owner is an individual the value will be `consumer`. The value will be `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + enum: + - business + - consumer + - unknown + example: "consumer" + xml: + attribute: true + caller-name: + type: string + description: "Full name of the person or business who owns the phone number. `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John Smith" + xml: + attribute: true + first-name: + type: string + description: "First name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John" + xml: + attribute: true + last-name: + type: string + description: "Last name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "Smith" + xml: + attribute: true + caller_name: + type: string + description: "Full name of the person or business who owns the phone number. `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John Smith" + last_name: + type: string + description: "Last name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "Smith" + firs_name: + # Key is not a typo. Do not change. + type: string + description: "First name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John" + caller_type: + type: string + description: "The value will be `business` if the owner of a phone number is a business. If the owner is an individual the value will be `consumer`. The value will be `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + enum: + - business + - consumer + - unknown + example: "consumer" + lookup_outcome: + type: object + x-nexmo-developer-collection-description-shown: true + description: "An object indicating whether all information about a phone number has been returned." + properties: + code: + description: | + Shows if all information about a phone number has been returned. Possible values: + + Code | Text + -- | -- + 0 | Success + 1 | Partial success - some fields populated + 2 | Failed + enum: + - 0 + - 1 + - 2 + example: "0" + xml: + attribute: true + lookup_outcome_message: + type: string + description: "Shows if all information about a phone number has been returned." + example: "Success" + xml: + x-text: true # see https://github.com/OAI/OpenAPI-Specification/issues/630 + reachable: + type: string + description: "Can you call `number` now. This is applicable to mobile numbers only." + enum: + - unknown + - reachable + - undeliverable + - absent + - bad_number + - blacklisted + example: "reachable" + roaming: + $ref: "#/components/schemas/niRoaming" + valid_number: + type: string + description: "Does `number` exist. `unknown` means the number could not be validated. `valid` means the number is valid. `not_valid` means the number is not valid. `inferred_not_valid` means that the number could not be determined as valid or invalid via an external system and the best guess is that the number is invalid. This is applicable to mobile numbers only." + enum: + - unknown + - valid + - not_valid + - inferred_not_valid + example: "valid" + ip_warnings: + type: string + description: "This property is deprecated and can safely be ignored." + example: "unknown" + + niResponseJsonBasic: + type: object + properties: + status: + $ref: "#/components/schemas/niBasicStatus" + status_message: + type: string + description: "The status description of your request." + example: "Success" + request_id: + type: string + description: "The unique identifier for your request. This is a alphanumeric string up to 40 characters." + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + maxLength: 40 + international_format_number: + type: string + description: "The `number` in your request in international format." + example: "447700900000" + national_format_number: + type: string + description: "The `number` in your request in the format used by the country the number belongs to." + example: "07700 900000" + country_code: + type: string + description: "Two character country code for `number`. This is in [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) format." + example: "GB" + pattern: "[A-Z]{2}" + country_code_iso3: + type: string + description: "Three character country code for `number`. This is in [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) format." + example: "GBR" + pattern: "[A-Z]{3}" + country_name: + type: string + description: "The full name of the country that `number` is registered in." + example: "United Kingdom" + country_prefix: + type: string + description: "The numeric prefix for the country that `number` is registered in." + example: "44" + + niResponseJsonStandard: + allOf: + - $ref: "#/components/schemas/niResponseJsonBasic" + - type: object + properties: + request_price: + type: string + description: "The amount in EUR charged to your account." + example: "0.04000000" + refund_price: + type: string + description: "If there is an internal lookup error, the `refund_price` will reflect the lookup price. If `cnam` is requested for a non-US number the `refund_price` will reflect the `cnam` price. If both of these conditions occur, `refund_price` is the sum of the lookup price and `cnam` price." + example: "0.01500000" + remaining_balance: + type: number + description: "Your account balance in EUR after this request. Not returned with Number Insight Advanced Async API." + example: "1.23456789" + current_carrier: + $ref: "#/components/schemas/niCurrentCarrierProperties" + original_carrier: + $ref: "#/components/schemas/niInitialCarrierProperties" + ported: + type: string + description: "If the user has changed carrier for `number`. The assumed status means that the information supplier has replied to the request but has not said explicitly that the number is ported." + enum: + - unknown + - ported + - not_ported + - assumed_not_ported + - assumed_ported + example: "not_ported" + roaming: + $ref: "#/components/schemas/niRoaming" + caller_identity: + $ref: "#/components/schemas/niCallerIdentity" + caller_name: + type: string + description: "Full name of the person or business who owns the phone number. `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John Smith" + last_name: + type: string + description: "Last name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "Smith" + first_name: + type: string + description: "First name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John" + caller_type: + type: string + description: "The value will be `business` if the owner of a phone number is a business. If the owner is an individual the value will be `consumer`. The value will be `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + enum: + - business + - consumer + - unknown + example: "consumer" + + niResponseJsonAdvanced: + type: object + description: Advanced + properties: + status: + $ref: "#/components/schemas/niStandardAdvancedStatus" + status_message: + type: string + description: "The status description of your request." + example: "Success" + request_id: + type: string + description: "The unique identifier for your request. This is a alphanumeric string up to 40 characters." + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + maxLength: 40 + international_format_number: + type: string + description: "The `number` in your request in international format." + example: "447700900000" + national_format_number: + type: string + description: "The `number` in your request in the format used by the country the number belongs to." + example: "07700 900000" + country_code: + type: string + description: "Two character country code for `number`. This is in [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) format." + example: "GB" + pattern: "[A-Z]{2}" + country_code_iso3: + type: string + description: "Three character country code for `number`. This is in [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) format." + example: "GBR" + pattern: "[A-Z]{3}" + country_name: + type: string + description: "The full name of the country that `number` is registered in." + example: "United Kingdom" + country_prefix: + type: string + description: "The numeric prefix for the country that `number` is registered in." + example: "44" + request_price: + type: string + description: "The amount in EUR charged to your account." + example: "0.04000000" + refund_price: + type: string + description: "If there is an internal lookup error, the `refund_price` will reflect the lookup price. If `cnam` is requested for a non-US number the `refund_price` will reflect the `cnam` price. If both of these conditions occur, `refund_price` is the sum of the lookup price and `cnam` price." + example: "0.01500000" + remaining_balance: + type: string + description: "Your account balance in EUR after this request. Not returned with Number Insight Advanced Async API." + example: "1.23456789" + current_carrier: + $ref: "#/components/schemas/niCurrentCarrierProperties" + original_carrier: + $ref: "#/components/schemas/niInitialCarrierProperties" + ported: + type: string + description: "If the user has changed carrier for `number`. The assumed status means that the information supplier has replied to the request but has not said explicitly that the number is ported." + enum: + - unknown + - ported + - not_ported + - assumed_not_ported + - assumed_ported + example: "not_ported" + roaming: + $ref: "#/components/schemas/niRoaming" + caller_identity: + $ref: "#/components/schemas/niCallerIdentity" + lookup_outcome: + type: integer + description: | + Shows if all information about a phone number has been returned. Possible values: + + Code | Text + --- | --- + 0 | Success + 1 | Partial success - some fields populated + 2 | Failed + enum: + - 0 + - 1 + - 2 + example: 0 + lookup_outcome_message: + type: string + description: "Shows if all information about a phone number has been returned." + example: "Success" + valid_number: + type: string + description: "Does `number` exist. `unknown` means the number could not be validated. `valid` means the number is valid. `not_valid` means the number is not valid. `inferred_not_valid` means that the number could not be determined as valid or invalid via an external system and the best guess is that the number is invalid. This is applicable to mobile numbers only." + enum: + - unknown + - valid + - not_valid + - inferred + - inferred_not_valid + example: "valid" + reachable: + type: string + description: "Can you call `number` now. This is applicable to mobile numbers only." + enum: + - unknown + - reachable + - undeliverable + - absent + - bad_number + - blacklisted + example: "reachable" + required: + - status + - status_message + - request_id + - international_format_number + - national_format_number + - country_code + - country_code_iso3 + - country_name + - country_prefix + + # niCarrier: + # type: object + # properties: + # network_code: + # type: string + # description: 'The [https://en.wikipedia.org/wiki/Mobile_country_code](https://en.wikipedia.org/wiki/Mobile_country_code) for the carrier`number` is associated with. Unreal numbers are marked as`unknown` and the request is rejected altogether if the number is impossible according to the [E.164](https://en.wikipedia.org/wiki/E.164) guidelines.' + # xml: + # attribute: true + # example: '12345' + # name: + # type: string + # description: 'The full name of the carrier that `number` is associated with.' + # xml: + # attribute: true + # example: 'Acme Inc' + # country: + # type: string + # description: 'The country that `number` is associated with. This is in ISO 3166-1 alpha-2 format.' + # xml: + # attribute: true + # example: 'GB' + # network_type: + # type: string + # description: 'The type of network that `number` is associated with.' + # enum: + # - mobile + # - landline + # - landline_premium + # - landline_tollfree + # - virtual + # - unknown + # - pager + # xml: + # attribute: true + # example: 'mobile' + + niCurrentCarrierProperties: + type: object + x-nexmo-developer-collection-description-shown: true + description: "Information about the network `number` is currently connected to." + properties: + network_code: + type: string + description: "The [https://en.wikipedia.org/wiki/Mobile_country_code](https://en.wikipedia.org/wiki/Mobile_country_code) for the carrier`number` is associated with. Unreal numbers are marked as`unknown` and the request is rejected altogether if the number is impossible according to the [E.164](https://en.wikipedia.org/wiki/E.164) guidelines." + xml: + attribute: true + example: "12345" + name: + type: string + description: "The full name of the carrier that `number` is associated with." + xml: + attribute: true + example: "Acme Inc" + country: + type: string + description: "The country that `number` is associated with. This is in ISO 3166-1 alpha-2 format." + xml: + attribute: true + example: "GB" + network_type: + type: string + description: "The type of network that `number` is associated with." + enum: + - mobile + - landline + - landline_premium + - landline_tollfree + - virtual + - unknown + - pager + xml: + attribute: true + example: "mobile" + + niInitialCarrierProperties: + type: object + x-nexmo-developer-collection-description-shown: true + description: "Information about the network `number` is currently connected to." + properties: + network_code: + type: string + description: "The [https://en.wikipedia.org/wiki/Mobile_country_code](https://en.wikipedia.org/wiki/Mobile_country_code) for the carrier`number` is associated with. Unreal numbers are marked as`unknown` and the request is rejected altogether if the number is impossible according to the [E.164](https://en.wikipedia.org/wiki/E.164) guidelines." + xml: + attribute: true + example: "12345" + name: + type: string + description: "The full name of the carrier that `number` is associated with." + xml: + attribute: true + example: "Acme Inc" + country: + type: string + description: "The country that `number` is associated with. This is in ISO 3166-1 alpha-2 format." + xml: + attribute: true + example: "GB" + network_type: + type: string + description: "The type of network that `number` is associated with." + enum: + - mobile + - landline + - landline_premium + - landline_tollfree + - virtual + - unknown + - pager + xml: + attribute: true + example: "mobile" + + niRoaming: + type: object + description: "Information about the roaming status for `number`. This is applicable to mobile numbers only." + properties: + status: + type: string + description: "Is `number` outside its home carrier network." + enum: + - unknown + - roaming + - not_roaming + example: roaming + xml: + attribute: true + roaming_country_code: + type: string + description: "If `number` is `roaming`, this is the [code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the country `number` is roaming in." + example: US + xml: + attribute: true + roaming_network_code: + type: string + description: "If `number` is `roaming`, this is the id of the carrier network `number` is roaming in." + example: "12345" + xml: + attribute: true + roaming_network_name: + type: string + description: "If `number` is `roaming`, this is the name of the carrier network `number` is roaming in." + example: "Acme Inc" + xml: + attribute: true + + niCallerIdentity: + type: object + description: "Information about the network `number` is currently connected to." + properties: + caller_type: + type: string + description: "The value will be `business` if the owner of a phone number is a business. If the owner is an individual the value will be `consumer`. The value will be `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + enum: + - business + - consumer + - unknown + example: "consumer" + caller_name: + type: string + description: "Full name of the person or business who owns the phone number. `unknown` if this information is not available. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John Smith" + first_name: + type: string + description: "First name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "John" + last_name: + type: string + description: "Last name of the person who owns the phone number if the owner is an individual. This parameter is only present if `cnam` had a value of `true` within the request." + example: "Smith" + + niBasicStatus: + type: integer + enum: + - 0 + - 1 + - 3 + - 4 + - 5 + - 9 + example: 0 + description: | + Code | Text + -- | -- + 0 | Success - request accepted for delivery by Nexmo. + 1 | Busy - you have made more requests in the last second than are permitted by your Nexmo account. Please retry. + 3 | Invalid - your request is incomplete and missing some mandatory parameters. + 4 | Invalid credentials - the _api_key_ or _api_secret_ you supplied is either not valid or has been disabled. + 5 | Internal Error - the format of the recipient address is not valid. + 9 | Partner quota exceeded - your Nexmo account does not have sufficient credit to process this request. + + niStandardAdvancedStatus: + type: integer + example: 0 + enum: + - 0 + - 1 + - 3 + - 4 + - 5 + - 9 + - 19 + - 43 + - 44 + - 45 + - 999 + description: | + Code | Text + -- | -- + 0 | Success - request accepted for delivery by Nexmo. + 1 | Busy - you have made more requests in the last second than are permitted by your Nexmo account. Please retry. + 3 | Invalid - your request is incomplete and missing some mandatory parameters. + 4 | Invalid credentials - the _api_key_ or _api_secret_ you supplied is either not valid or has been disabled. + 5 | Internal Error - the format of the recipient address is not valid. + 9 | Partner quota exceeded - your Nexmo account does not have sufficient credit to process this request. + 19 | Facility Not Allowed - your request makes use of a facility that is not enabled on your account. + 43, 44, 45 | Live mobile lookup not returned. Not all return parameters are available. + 999 | Request unparseable. + + securitySchemes: + apiKey: + type: apiKey + name: api_key + in: query + description: "You can find your API key in your [account overview](https://dashboard.nexmo.com/account-overview)" + apiSecret: + type: apiKey + name: api_secret + in: query + description: "You can find your API secret in your [account overview](https://dashboard.nexmo.com/account-overview)" diff --git a/tests/resources/definitions/numbers.yml b/tests/resources/definitions/numbers.yml new file mode 100755 index 00000000..edbe2829 --- /dev/null +++ b/tests/resources/definitions/numbers.yml @@ -0,0 +1,477 @@ +openapi: 3.0.0 +info: + title: Numbers API + version: 1.0.15 + description: >- + The Numbers API enables you to manage your existing numbers and buy new virtual numbers for + use with Nexmo's APIs. Further information is here: + contact: + name: Nexmo.com + email: devrel@nexmo.com + url: "https://developer.nexmo.com" + x-twitter: Nexmo + termsOfService: "https://www.nexmo.com/terms-of-use" + license: + name: The MIT License (MIT) + url: "https://opensource.org/licenses/MIT" + x-logo: + url: "https://twitter.com/Nexmo/profile_image?size=original" + x-apiClientRegistration: "https://dashboard.nexmo.com/sign-up" +servers: + - url: "https://rest.nexmo.com" +externalDocs: + description: Numbers product documentation on the Nexmo Developer Portal + url: "https://developer.nexmo.com/numbers/overview" +security: + - apiKey: [] + apiSecret: [] +paths: + "/account/numbers": + get: + operationId: getOwnedNumbers + summary: List the numbers you own + description: Retrieve all the inbound numbers associated with your Nexmo account. + parameters: + - $ref: "#/components/parameters/application_id" + - $ref: "#/components/parameters/has_application" + - name: country + required: false + in: query + schema: + $ref: "#/components/schemas/country" + - $ref: "#/components/parameters/pattern" + - $ref: "#/components/parameters/search_pattern" + - $ref: "#/components/parameters/size" + - $ref: "#/components/parameters/index" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/inbound-numbers" + text/xml: + schema: + $ref: "#/components/schemas/inbound-numbers" + "401": + description: Unauthorized + content: + application/json: + schema: + type: object + properties: + error-code: + type: string + description: A code relating to this error + example: "401" + error-code-label: + type: string + description: Words describing the error that occurred + example: authentication failed + + "/number/search": + get: + operationId: getAvailableNumbers + summary: Search available numbers + description: Retrieve inbound numbers that are available for the specified country. + parameters: + - $ref: "#/components/parameters/country" + - $ref: "#/components/parameters/type" + - $ref: "#/components/parameters/pattern" + - $ref: "#/components/parameters/search_pattern" + - name: features + required: false + in: query + description: >- + Available features are `SMS` and `VOICE`. To look for numbers that support both, use a + comma-separated value: `SMS,VOICE`. + schema: + type: string + enum: + - SMS + - VOICE + - "SMS,VOICE" + example: SMS + - $ref: "#/components/parameters/size" + - $ref: "#/components/parameters/index" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/available-numbers" + text/xml: + schema: + $ref: "#/components/schemas/available-numbers" + "401": + description: Unauthorized + "/number/buy": + post: + operationId: buyANumber + summary: Buy a number + description: Request to purchase a specific inbound number. + requestBody: + description: Number details + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/number-details" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/response" + text/xml: + schema: + $ref: "#/components/schemas/response" + "401": + description: Unauthorized + "/number/cancel": + post: + operationId: cancelANumber + summary: Cancel a number + description: Cancel your subscription for a specific inbound number. + requestBody: + description: Number details + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/number-details" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/response" + text/xml: + schema: + $ref: "#/components/schemas/response" + "401": + description: Unauthorized + "/number/update": + post: + operationId: updateANumber + summary: Update a number + description: Change the behaviour of a number that you own. + requestBody: + description: Number details + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/number-details-update" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/response" + text/xml: + schema: + $ref: "#/components/schemas/response" + "401": + description: Unauthorized +components: + securitySchemes: + apiKey: + type: apiKey + name: api_key + in: query + description: "You can find your API key in the [developer dashboard](https://dashboard.nexmo.com)" + apiSecret: + type: apiKey + name: api_secret + in: query + description: "You can find your API secret in the [developer dashboard](https://dashboard.nexmo.com)" + parameters: + index: + name: index + required: false + in: query + description: Page index + schema: + type: integer + default: 1 + example: 1 + size: + name: size + required: false + in: query + description: Page size + schema: + type: integer + maximum: 100 + default: 10 + example: 10 + pattern: + name: pattern + required: false + in: query + description: The number pattern you want to search for. Use in conjunction with `search_pattern`. + schema: + type: string + example: "12345" + search_pattern: + name: search_pattern + required: false + in: query + description: | + The strategy you want to use for matching: + + + * `0` - Search for numbers that start with `pattern` + * `1` - Search for numbers that contain `pattern` + * `2` - Search for numbers that end with `pattern` + schema: + type: integer + default: 0 + enum: + - 0 + - 1 + - 2 + example: 1 + has_application: + name: has_application + required: false + in: query + description: | + Set this optional field to `true` to restrict your results to numbers + associated with an application (any application). Set to `false` to + find all numbers not associated with any application. Omit the field + to avoid filtering on whether or not the number is assigned to an + application. + schema: + type: boolean + example: false + application_id: + name: application_id + required: false + in: query + description: The application that you want to return the numbers for. + schema: + type: string + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + country: + name: country + in: query + required: true + description: The two character country code to filter on (in ISO 3166-1 alpha-2 format) + schema: + $ref: "#/components/schemas/country" + type: + name: type + in: query + required: false + description: Set this parameter to filter the type of number, such as mobile or landline + schema: + type: string + enum: + - landline + - mobile-lvn + - landline-toll-free + example: mobile-lvn + msisdn: + name: msisdn + required: true + in: query + description: The inbound number you want to cancel, in [E.164 international format](/concepts/guides/glossary#e-164-format) + schema: + type: string + example: "447700900000" + + schemas: + country: + type: string + minLength: 2 + maxLength: 2 + example: GB + description: The two character country code in ISO 3166-1 alpha-2 format + ownednumber: + type: object + properties: + country: + $ref: '#/components/schemas/country' + msisdn: + type: string + example: "447700900000" + description: An available inbound virtual number. + moHttpUrl: + type: string + format: url + example: "https://example.com/webhooks/inbound-sms" + description: >- + The URL of the webhook endpoint that handles inbound messages + type: + type: string + example: mobile-lvn + description: "The type of number: `landline`, `landline-toll-free` or `mobile-lvn`" + features: + type: array + items: + type: string + example: [VOICE, SMS] + description: "The capabilities of the number: `SMS` or `VOICE` or `SMS,VOICE`" + messagesCallbackType: + type: string + example: app + description: "The messages webhook type: always `app`" + messagesCallbackValue: + type: string + example: aaaaaaaa-bbbb-cccc-dddd-0123456789ab + description: A Nexmo Application ID + voiceCallbackType: + type: string + example: app + description: "The voice webhook type: `sip`, `tel`, or `app`" + voiceCallbackValue: + type: string + example: aaaaaaaa-bbbb-cccc-dddd-0123456789ab + description: A SIP URI, telephone number or Application ID + availablenumber: + type: object + properties: + country: + $ref: '#/components/schemas/country' + msisdn: + type: string + example: "447700900000" + description: An available inbound virtual number. + type: + type: string + example: mobile-lvn + description: "The type of number: `landline`, `landline-toll-free` or `mobile-lvn`" + cost: + type: string + example: 1.25 + description: The monthly rental cost for this number, in Euros + features: + type: array + items: + type: string + example: [VOICE, SMS] + description: "The capabilities of the number: `SMS` or `VOICE` or `SMS,VOICE`" + inbound-numbers: + type: object + properties: + count: + type: integer + description: The total amount of numbers owned by the account + example: 1 + numbers: + type: array + description: A paginated array of numbers and their details + items: + $ref: "#/components/schemas/ownednumber" + available-numbers: + type: object + xml: + name: inbound-numbers + properties: + count: + type: integer + description: The total amount of numbers available in the pool. + example: 1234 + numbers: + type: array + description: A paginated array of available numbers and their details. + items: + $ref: "#/components/schemas/availablenumber" + + number-details: + type: object + properties: + country: + $ref: '#/components/schemas/country' + msisdn: + type: string + example: "447700900000" + description: An available inbound virtual number. + target_api_key: + type: string + example: "1a2345b7" + description: If you’d like to perform an action on a subaccount, provide the `api_key` of that account here. If you’d like to perform an action on your own account, you do not need to provide this field. + required: + - country + - msisdn + + number-details-update: + type: object + properties: + country: + $ref: '#/components/schemas/country' + msisdn: + type: string + example: "447700900000" + description: An available inbound virtual number. + moHttpUrl: + description: >- + An URL-encoded URI to the webhook endpoint that handles inbound + messages. Your webhook endpoint must be active before you make this + request. Nexmo makes a `GET` request to the endpoint and checks + that it returns a `200 OK` response. Set this parameter's value to an empty string to remove the webhook. + type: string + example: "https://example.com/webhooks/inbound-sms" + moSmppSysType: + description: The associated system type for your SMPP client + type: string + example: inbound + voiceCallbackType: + description: >- + Specify whether inbound voice calls on your number are handled by + your Application configuration, or forwarded to a SIP or a telephone number. + This must be used with the `voiceCallbackValue` parameter. + type: string + enum: + - sip + - tel + - app + example: app + voiceCallbackValue: + description: >- + A SIP URI, telephone number or Application ID. Must be used + with the `voiceCallbackType` parameter. + type: string + example: "447700900000" + voiceStatusCallback: + description: A webhook URI for Nexmo to send a request to when a call ends + type: string + example: "https://example.com/webhooks/status" + messagesCallbackType: + description: >- + DEPRECATED - We recommend that you use `voiceCallbackType` instead. + Specifies the Messages webhook type (always `app`) associated with this + number and must be used with the `messagesCallbackValue` parameter. + type: string + enum: + - app + example: app + deprecated: true + messagesCallbackValue: + description: >- + DEPRECATED - We recommend that you use `voiceCallbackValue` instead. + Specifies the application of ID of your Messages application. + It must be used with the `messagesCallbackType` parameter. + type: string + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + deprecated: true + required: + - country + - msisdn + response: + type: object + properties: + error-code: + type: string + example: "200" + description: The status code of the response. `200` indicates a successful request. + error-code-label: + type: string + example: success + description: The status code description diff --git a/tests/resources/definitions/pricing.yml b/tests/resources/definitions/pricing.yml new file mode 100755 index 00000000..0418e19a --- /dev/null +++ b/tests/resources/definitions/pricing.yml @@ -0,0 +1,311 @@ +openapi: 3.0.0 +servers: + - url: "https://rest.nexmo.com/account" +info: + version: "0.0.2" + title: Pricing API + description: >- + The API to retrieve pricing information. + + Please note the Pricing API is rate limited to one request per second. + + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: "https://developer.nexmo.com/" +paths: + /get-pricing/outbound/{type}: + get: + summary: Retrieve outbound pricing for a specific country. + operationId: retrievePricingCountry + description: > + Retrieves the pricing information based on the specified country. + tags: + - Pricing + security: + - basicAuth: [] + parameters: + - $ref: "#/components/parameters/type" + - $ref: "#/components/parameters/api_key" + - $ref: "#/components/parameters/api_secret" + - name: country + in: query + description: A two letter [country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). For example, `CA`. + required: true + schema: + type: string + responses: + "200": + description: Pricing information for a specific country. + content: + application/json: + schema: + $ref: "#/components/schemas/PricingCountryResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "401": + $ref: "#/components/responses/UnauthorizedError" + "404": + $ref: "#/components/responses/NotFoundError" + "429": + $ref: "#/components/responses/TooManyRequestsError" + /get-full-pricing/outbound/{type}: + get: + summary: Retrieve outbound pricing for all countries. + operationId: retrievePricingAllCountries + description: > + Retrieves the pricing information for all countries. + tags: + - Pricing + security: + - basicAuth: [] + parameters: + - $ref: "#/components/parameters/type" + - $ref: "#/components/parameters/api_key" + - $ref: "#/components/parameters/api_secret" + responses: + "200": + description: Pricing response + content: + application/json: + schema: + $ref: "#/components/schemas/PricingCountriesResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "401": + $ref: "#/components/responses/UnauthorizedError" + "404": + $ref: "#/components/responses/NotFoundError" + "429": + $ref: "#/components/responses/TooManyRequestsError" + + /get-prefix-pricing/outbound/{type}: + get: + summary: Retrieve outbound pricing for a specific dialing prefix. + operationId: retrievePrefixPricing + description: > + Retrieves the pricing information based on the dialing prefix. + tags: + - Pricing + security: + - basicAuth: [] + parameters: + - $ref: "#/components/parameters/type" + - $ref: "#/components/parameters/api_key" + - $ref: "#/components/parameters/api_secret" + - name: prefix + in: query + description: "The numerical dialing prefix to look up pricing for. Examples include 44, 1 and so on." + required: true + schema: + type: string + responses: + "200": + description: Pricing countries response + content: + application/json: + schema: + $ref: "#/components/schemas/PricingCountriesResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "401": + $ref: "#/components/responses/UnauthorizedError" + "404": + $ref: "#/components/responses/NotFoundError" + "429": + $ref: "#/components/responses/TooManyRequestsError" +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + responses: + UnauthorizedError: + description: You did not provide valid credentials + content: + application/json: + schema: + type: object + required: + - currency + - code + - error-code-label + properties: + currency: + type: string + example: "EUR" + code: + type: string + example: "401" + error-code-label: + type: string + example: "authentication failed" + BadRequestError: + description: Bad request. You probably provided an invalid parameter. + content: + application/json: + schema: + type: object + required: + - type + - error_title + - invalid_parameters + properties: + type: + type: string + example: "BAD_REQUEST" + error_title: + type: string + example: "Bad Request" + invalid_parameters: + type: object + properties: + parameter: + type: string + example: "country" + message: + type: string + example: "Is required" + TooManyRequestsError: + description: You made too many requests. The API is rate limited to one request per second. + NotFoundError: + description: The page you requested was not found + + parameters: + api_key: + name: api_key + in: query + description: Your Nexmo API key. + required: true + schema: + type: string + api_secret: + name: api_secret + in: query + description: Your Nexmo API secret. + required: true + schema: + type: string + type: + name: type + in: path + description: "The type of service you wish to retrieve data about: either `sms`, `sms-transit` or `voice`." + required: true + example: sms + schema: + type: string + schemas: + PricingCountryResponse: + properties: + countryCode: + example: "CA" + type: string + description: >- + Two letter country code. + countryName: + type: string + example: "Canada" + description: >- + Readable country name. + countryDisplayName: + type: string + example: "Canada" + description: >- + Readable country name. + currency: + type: string + example: "EUR" + description: >- + The currency that your account is being billed in (by default `Euros—EUR`). Can change in the Dashboard to US Dollars—USD. + defaultPrice: + type: string + example: "0.00620000" + description: >- + The default price. + dialingPrefix: + type: string + example: "1" + description: >- + The dialling prefix. + networks: + type: array + description: An array of network objects + items: + $ref: "#/components/schemas/NetworkObject" + PricingCountriesResponse: + properties: + count: + example: "243" + type: string + description: >- + The number of countries retrieved. + countries: + type: array + description: >- + A list of countries. + items: + $ref: "#/components/schemas/CountryObject" + CountryObject: + type: object + properties: + countryName: + type: string + example: "Canada" + description: >- + Readable country name. + countryDisplayName: + type: string + example: "Canada" + description: >- + Readable country name. + currency: + type: string + example: "EUR" + description: >- + The currency that your account is being billed in (by default `Euros—EUR`). Can change in the Dashboard to US Dollars—USD. + defaultPrice: + type: string + example: "0.00620000" + description: >- + The default price. + dialingPrefix: + type: string + example: "1" + description: >- + The dialling prefix. + networks: + type: array + description: An array of network objects + items: + $ref: "#/components/schemas/NetworkObject" + NetworkObject: + type: object + properties: + type: + type: string + example: "mobile" + description: "The type of network: mobile or landline." + price: + type: string + example: "0.00590000" + description: "The cost to send a message or make a call to this network" + currency: + type: string + example: "EUR" + description: "The currency used for prices for this network." + mcc: + type: string + example: "302" + description: "The [Mobile Country Code](https://en.wikipedia.org/wiki/Mobile_country_code) of the operator." + mnc: + type: string + example: "530" + description: "The Mobile Network Code of the operator." + networkCode: + type: string + example: "302530" + description: "The Mobile Country Code and Mobile Network Code combined to give a unique reference for the operator." + networkName: + type: string + example: "Keewaytinook Okimakanak" + description: "The company/organisational name of the operator." diff --git a/tests/resources/definitions/redact.yml b/tests/resources/definitions/redact.yml new file mode 100755 index 00000000..a9e561e6 --- /dev/null +++ b/tests/resources/definitions/redact.yml @@ -0,0 +1,284 @@ +--- +openapi: "3.0.0" +info: + version: 1.0.4 + title: "Redact API" + description: Nexmo provides [Redact API](https://developer.nexmo.com/redact/overview) to help organisations meet their privacy compliance obligations. Redact API allows you to redact private information on demand, allowing you to meet your own compliance needs. + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: "https://developer.nexmo.com/" +servers: + - url: https://api.nexmo.com/v1/redact +paths: + /transaction: + post: + summary: Redact a specific message + operationId: redact-message + description: "" + security: + - basicAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RedactTransaction" + responses: + "204": + description: "Success" + "401": + description: "Authentication failure" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorUnauthorized" + "403": + description: "Authorisation denied" + content: + application/json: + schema: + oneOf: + - $ref: "#/components/schemas/ErrorPrematureRedaction" + - $ref: "#/components/schemas/ErrorUnprovisioned" + "404": + description: "No such record" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorInvalidId" + "422": + description: "Invalid JSON body" + content: + application/json: + schema: + oneOf: + - $ref: "#/components/schemas/ErrorInvalidJson" + - $ref: "#/components/schemas/ErrorUnsupportedProduct" + "429": + description: "Rate Limited" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorThrottled" + +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + schemas: + ErrorUnsupportedProduct: + description: Unsupported Product + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + description: Link to error / remediation options + example: https://developer.nexmo.com/api-errors/redact#invalid-product + title: + type: string + description: Generic error message + example: Invalid Product + detail: + type: string + description: Additional information about the error + example: No product corresponding to supplied string sms2! + instance: + type: string + description: Internal Trace ID + example: bf0ca0bf927b3b52e3cb03217e1a1ddf + + ErrorPrematureRedaction: + description: Premature Redaction + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + description: Link to error / remediation options + example: https://developer.nexmo.com/api-errors/redact#premature-redaction + title: + type: string + description: Generic error message + example: Premature Redaction + detail: + type: string + description: Additional information about the error + example: You must wait 60 minutes before redacting ID '0A000000B0C9A1234' + instance: + type: string + description: Internal Trace ID + example: bf0ca0bf927b3b52e3cb03217e1a1ddf + + ErrorUnprovisioned: + description: Unprovisioned + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + description: Link to error / remediation options + example: https://developer.nexmo.com/api-errors#unprovisioned + title: + type: string + description: Generic error message + example: Authorisation error + detail: + type: string + description: Additional information about the error + example: User=ABC123 is not provisioned to redact product=SMS + instance: + type: string + description: Internal Trace ID + example: bf0ca0bf927b3b52e3cb03217e1a1ddf + + ErrorUnauthorized: + type: object + required: + - type + - error_title + properties: + type: + type: string + description: Machine readable error type + example: UNAUTHORIZED + error_title: + type: string + description: Error title + example: Unauthorized + + ErrorInvalidJson: + description: Invalid JSON + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + description: Link to error / remediation options + example: https://developer.nexmo.com/api-errors#invalid-json + title: + type: string + description: Generic error message + example: Invalid JSON + detail: + type: string + description: Additional information about the error + example: 'Unexpected character (''"'' (code 34)): was expecting comma to separate Object entries' + instance: + type: string + description: Internal Trace ID + example: bf0ca0bf927b3b52e3cb03217e1a1ddf + + ErrorInvalidId: + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + description: Link to error / remediation options + example: https://developer.nexmo.com/api-errors#invalid-id + title: + type: string + description: Generic error message + example: Invalid ID + detail: + type: string + description: Additional information about the error + example: ID '0A000000B0C9A1234' could not be found (type=MT) + instance: + type: string + description: Internal Trace ID + example: bf0ca0bf927b3b52e3cb03217e1a1ddf + + ErrorThrottled: + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + description: Link to error / remediation options + example: https://developer.nexmo.com/api-errors/redact#rate-limit + title: + type: string + description: Generic error message + example: Rate Limit Hit + detail: + type: string + description: Additional information about the error + example: Please wait, then retry your request + instance: + type: string + description: Internal Trace ID + example: bf0ca0bf927b3b52e3cb03217e1a1ddf + + RedactTransaction: + type: object + required: + - id + - product + - type + properties: + id: + type: string + description: The transaction ID to redact + product: + type: string + example: sms + description: Product name that the ID provided relates to + enum: + - sms + - voice + - number-insight + - verify + - verify-sdk + - messages + type: + type: string + enum: + - inbound + - outbound + example: outbound + default: outbound + description: Required if redacting SMS data + +x-errors: + invalid-product: + description: The provided product is invalid + resolution: Modify your request to provide a valid product + link: + text: View API reference + url: /api/redact#transaction + + premature-redaction: + description: There is a 1 hour time period before data can be redacted to allow for all data to propagate through the Nexmo system + resolution: Wait until the data becomes valid for redaction + + rate-limit: + description: The request was rate limited + resolution: The Redact API supports 170 requests per second. Slow down your request rate diff --git a/tests/resources/definitions/reports.yml b/tests/resources/definitions/reports.yml new file mode 100755 index 00000000..80a8cc1a --- /dev/null +++ b/tests/resources/definitions/reports.yml @@ -0,0 +1,1389 @@ +openapi: "3.0.0" +info: + version: 2.0.8 + title: Nexmo Reports API + description: > + Nexmo's Reports API allows you to request a report of activity on your Nexmo account.
+ On average the Reports API takes 5 - 10 minutes to generate 1 million records. Nexmo recommends requesting only reports of up to a maximum of 20 million records, by setting the start and end dates accordingly.
+ Reports are generated as CSV files compressed in zip archives. + contact: + name: Nexmo.com + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' + x-twitter: Nexmo + x-label: Beta + termsOfService: 'https://www.nexmo.com/terms-of-use' + license: + name: The MIT License (MIT) + url: 'https://opensource.org/licenses/MIT' + x-logo: + url: 'https://twitter.com/Nexmo/profile_image?size=original' + x-apiClientRegistration: 'https://dashboard.nexmo.com/sign-up' +servers: + - url: https://api.nexmo.com +paths: + "/v2/reports": + post: + operationId: create-report + summary: Create a report + description: Request a report on your account activity + security: + - bearerAuth: [] + - basicAuth: [] + requestBody: + description: > + The parameters of the JSON body will be used to create and filter the report.
+ The value of the `product` field will define which product the report will be created for and which parameters are accepted. + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/SMS' + - $ref: '#/components/schemas/VOICE-CALL' + - $ref: '#/components/schemas/VERIFY-API' + - $ref: '#/components/schemas/NUMBER-INSIGHT' + - $ref: '#/components/schemas/MESSAGES' + - $ref: '#/components/schemas/CONVERSATIONS' + discriminator: + propertyName: product + responses: + '200': + $ref: '#/components/responses/report_responses_create_report' + '400': + $ref: '#/components/responses/400_json' + '401': + $ref: '#/components/responses/401' + '403': + $ref: '#/components/responses/403' + '422': + $ref: '#/components/responses/422' + get: + operationId: list-reports + summary: List reports + description: List reports created by the specified account based on filtered provided. + security: + - bearerAuth: [] + - basicAuth: [] + parameters: + - name: account_id + description: The account for which the list of reports will be queried. + in: query + schema: + type: string + minLength: 8 + maxLength: 8 + example: abcd1234 + required: true + - name: status + description: List of statuses of reports to list, comma separated. Statuses can be one of PENDING, PROCESSING, SUCCESS, ABORTED, FAILED, TRUNCATED. + in: query + schema: + type: string + example: SUCCESS, FAILED + - name: date_from + description: ISO-8601 extended time zone offset or ISO-8601 UTC zone offset formatted date from which the list of reports will be queried. Format yyyy-mm-ddThh:mm:ss[.sss]±hh:mm or yyyy-mm-ddThh:mm:ss[.sss]Z + in: query + schema: + type: string + format: date + example: "2019-06-28T00:00:00-00:00" + - name: date_to + description: ISO-8601 extended time zone offset or ISO-8601 UTC zone offset formatted date until which the list of reports will be queried. Format yyyy-mm-ddThh:mm:ss[.sss]±hh:mm or yyyy-mm-ddThh:mm:ss[.sss]Z + in: query + schema: + type: string + format: date + example: "2019-06-28T23:59:59-00:00" + responses: + '200': + $ref: '#/components/responses/list_report_response' + '400': + $ref: '#/components/responses/400_params' + '401': + $ref: '#/components/responses/401' + "/v2/reports/{report_id}": + get: + operationId: get-report + security: + - bearerAuth: [] + - basicAuth: [] + summary: Get status of report + description: Retrieve status and metadata about a requested report. + parameters: + - name: report_id + in: path + schema: + type: string + example: aaaaaaaa-bbbb-cccc-dddd-0123456789ab + description: UUID of the report + required: true + responses: + '200': + $ref: '#/components/responses/report_responses' + '401': + $ref: '#/components/responses/401' + '404': + $ref: '#/components/responses/404' + delete: + operationId: cancel-report + security: + - bearerAuth: [] + - basicAuth: [] + summary: Cancel the execution of a report + description: Cancel the execution of a pending or processing report. + parameters: + - name: report_id + in: path + schema: + type: string + example: aaaaaaaa-bbbb-cccc-dddd-0123456789ab + description: UUID of the report + required: true + responses: + '200': + $ref: '#/components/responses/report_responses' + '401': + $ref: '#/components/responses/401' + '404': + $ref: '#/components/responses/404' + "/v3/media/{file_id}": + get: + operationId: download-report + security: + - bearerAuth: [] + - basicAuth: [] + summary: Get report data + description: > + Download a zipped archive of the rendered report. The file is available for download for 72 hours.
+ The zip file will be named `_.zip`
+ The csv file in the zip archive will be named as `report___.csv` the date will be formatted as `yyyyMMdd`. + parameters: + - name: file_id + in: path + schema: + type: string + example: aaaaaaaa-bbbb-cccc-dddd-0123456789ab + description: UUID of the file. + required: true + responses: + '200': + $ref: '#/components/responses/csv_report_response' + '401': + $ref: '#/components/responses/401' + '404': + $ref: '#/components/responses/404' +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + basicAuth: + type: http + scheme: basic + + responses: + report_responses: + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/sms_response_get_report' + - $ref: '#/components/schemas/voice_call_response_get_report' + - $ref: '#/components/schemas/verify_api_response_get_report' + - $ref: '#/components/schemas/number_insight_response_get_report' + - $ref: '#/components/schemas/messages_response_get_report' + - $ref: '#/components/schemas/conversations_response_get_report' + report_responses_create_report: + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/sms_response_create_report' + - $ref: '#/components/schemas/voice_call_response_create_report' + - $ref: '#/components/schemas/verify_api_response_create_report' + - $ref: '#/components/schemas/number_insight_response_create_report' + - $ref: '#/components/schemas/messages_response_create_report' + - $ref: '#/components/schemas/conversations_response_create_report' + + list_report_response: + description: OK + content: + application/json: + schema: + type: object + properties: + items_count: + type: integer + description: The number of reports in the list. + self_link: + type: string + format: uri + description: URI of this search. + example: "https://api.nexmo.com/v2/reports/" + reports: + type: array + description: The list of reports. + items: + type: object + anyOf: + - $ref: '#/components/schemas/sms_response_get_report' + - $ref: '#/components/schemas/voice_call_response_get_report' + - $ref: '#/components/schemas/verify_api_response_get_report' + - $ref: '#/components/schemas/number_insight_response_get_report' + - $ref: '#/components/schemas/messages_response_get_report' + - $ref: '#/components/schemas/conversations_response_get_report' + + csv_report_response: + description: OK + content: + "application/octet-stream": + schema: + type: object + properties: + report: + type: array + description: The report in CSV format inside the zip archive. + items: + type: object + anyOf: + - $ref: '#/components/schemas/csv_sms_outbound' + - $ref: '#/components/schemas/csv_sms_inbound' + - $ref: '#/components/schemas/csv_voice' + - $ref: '#/components/schemas/csv_verify' + - $ref: '#/components/schemas/csv_ni' + - $ref: '#/components/schemas/csv_messages_outbound' + - $ref: '#/components/schemas/csv_messages_inbound' + - $ref: '#/components/schemas/csv_conversations_ip_voice' + + 400_json: + description: Bad Request + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#invalid-json' + title: + type: string + example: 'Malformed JSON' + detail: + type: string + example: 'Malformed JSON payload.' + instance: + type: string + example: f94b4e56604e07e5e5ad5a7228618f81 + 400_params: + description: Bad Request + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors/reports#invalid-request-parameters' + title: + type: string + example: 'Invalid request parameter' + detail: + type: string + example: 'Invalid request parameter.' + instance: + type: string + example: f94b4e56604e07e5e5ad5a7228618f81 + "401": + description: Unauthorized + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#unauthorized' + title: + type: string + example: 'Unauthorized' + detail: + type: string + example: 'You did not provide correct credentials.' + instance: + type: string + example: f94b4e56604e07e5e5ad5a7228618f81 + "403": + description: Forbidden + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#forbidden' + title: + type: string + example: 'Forbidden' + detail: + type: string + example: 'User not authorized to query the requested data.' + instance: + type: string + example: f94b4e56604e07e5e5ad5a7228618f81 + "404": + description: Not found + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#not-found' + title: + type: string + example: 'Invalid report ID' + detail: + type: string + example: 'Report aaaaaaaa-bbbb-cccc-dddd-0123456789ab does not exist, or you do not have access.' + instance: + type: string + example: f94b4e56604e07e5e5ad5a7228618f81 + "422": + description: Unprocessable entity + content: + application/json: + schema: + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#not-found' + title: + type: string + example: 'Unprocessable entity' + detail: + type: string + example: 'Unprocessable entity.' + instance: + type: string + example: f94b4e56604e07e5e5ad5a7228618f81 + + schemas: + # request fields + product: + type: string + description: Which product you wish to generate a report for. + example: "SMS" + enum: + - SMS + - VOICE-CALL + - VERIFY-API + - NUMBER-INSIGHT + - MESSAGES + - CONVERSATIONS + product_sms: + type: string + example: "SMS" + product_voice: + type: string + example: "VOICE-CALL" + product_verify: + type: string + example: "VERIFY-API" + product_ni: + type: string + example: "NUMBER-INSIGHT" + product_messages: + type: string + example: "MESSAGES" + product_conversations: + type: string + example: "CONVERSATIONS" + direction: + type: string + description: Direction of the request, either inbound or outbound. + enum: + - inbound + - outbound + example: "outbound" + account_id: + type: string + description: The account ID (API key) you wish to search for. This can differ from the API key in the authorization header because some accounts can request reports for other accounts, e.g. a primary account owner wants to create a report for one of its subaccounts. + example: "abcdef01" + include_subaccounts: + type: boolean + description: Whether to include subaccounts or not. + enum: + - true + - false + example: "false" + default: false + callback_url: + type: string + description: URL to send a callback upon completion of the report. This POST callback contains the same information as the response to [Get status of report](/api/reports#get-report). + format: uri + example: "https://requestb.in/12345" + date_start: + type: string + format: date + description: > + ISO-8601 extended time zone offset or ISO-8601 UTC zone offset formatted date (format yyyy-mm-ddThh:mm:ss[.sss]±hh:mm or yyyy-mm-ddThh:mm:ss[.sss]Z) for when reports + should begin. It filters on the time the API call was received by Nexmo and corresponds to the field date_received (date_start for Voice) in the report file. It is + inclusive, i.e. the provided value is less than or equal to the value in the field date_received (date_start for Voice) in the CDR.
If unspecified, defaults + to seven days ago. + example: "2017-12-01T00:00:00+00:00" + date_end: + type: string + format: date + description: > + ISO-8601 extended time zone offset or ISO-8601 UTC zone offset formatted date (format yyyy-mm-ddThh:mm:ss[.sss]±hh:mm or yyyy-mm-ddThh:mm:ss[.sss]Z) for when report should end. + It is exclusive, i.e. the provided value is strictly greater than the value in the field date_received in the CDR.
If unspecified, defaults to the current time. + example: "2018-01-01T00:00:00+00:00" + client_ref: + type: string + description: Search for messages with this `client_ref`. + maxLength: 40 + minLength: 40 + account_ref: + type: string + description: Search for messages with this `account_ref`. + maxLength: 40 + minLength: 40 + include_message: + type: boolean + description: Include the text of messages in the report. + example: "true" + default: false + to: + type: string + description: Request to this number. + example: "441234567890" + from: + type: string + description: Request from this number. + example: "441234567890" + sms_status: + type: string + description: Search only for sms with the corresponding status. + enum: + - delivered + - expired + - failed + - rejected + - accepted + - buffered + - unknown + example: "delivered" + voice_status: + type: string + description: Search only for voice call with the corresponding status. + example: "started" + enum: + - started + - ringing + - answered + - machine + - completed + - timeout + - failed + - rejected + - cancelled + - busy + application_id: + type: string + description: Search only for requests attached to a particular Application ID. + conversation_type: + type: string + description: Type of conversation request. + example: ip-voice + enum: + - ip-voice + conversation_id: + type: string + example: CON-abc123 + description: Search only for Voice calls attached to this particular Conversation. This should include the "CON-" prefix. + status: + type: string + description: Search only for conversation events with the specified event status. + example: "0" + number: + type: string + description: Search only request for the target number. + example: "441234567890" + uuid: + type: string + description: Search only messages with the specified uuid. + example: "441234567890" + # response fields + request_id: + type: string + description: UUID of the request. + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + request_status: + type: string + description: Status of the request. + enum: + - PENDING + - PROCESSING + - SUCCESS + - ABORTED + - FAILED + - TRUNCATED + example: SUCCESS + request_status_create_report: + type: string + description: Status of the request. + enum: + - PENDING + - PROCESSING + - SUCCESS + - ABORTED + - FAILED + - TRUNCATED + example: PENDING + file_id: + type: string + description: Id of the generated report file. + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + receive_time: + type: string + format: date + description: Time at which the report request was received by the service. + example: "2019-06-28T15:30:00+0000" + start_time: + type: string + format: date + description: Time at which the report processing of the report has started. + example: "2019-06-28T15:30:00+0000" + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + format: uri + description: URI of this document. + example: "https://api.nexmo.com/v2/reports/aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + download_report: + type: object + properties: + href: + type: string + format: uri + description: URL pointing to the generated report. + example: "https://api.nexmo.com/v3/media/aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + _links_create_report: + type: object + properties: + self: + type: object + properties: + href: + type: string + format: uri + description: URI of this document. + example: "https://api.nexmo.com/v2/reports/aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + items_count: + type: integer + description: The number of rows in the resulting file (when report has been completed). + # csv fields + message_id: + type: string + description: Id of the request. + example: "0000000A" + message_body: + type: string + description: Body of the message sent. Only present if include_message parameter is set to true. + example: "This is a text message" + network: + type: string + description: Network used to send the request. + example: "23415" + network_name: + type: string + description: Name of the network used to send the request. + example: "Vodafone Limited" + country: + type: string + description: Country where the request was sent. + example: "FR" + country_name: + type: string + description: Country name where the request was sent. + example: "France" + date: + type: string + format: date + description: Date of the request. + example: "2019-06-28T15:30:00+0000" + latency: + type: integer + description: Latency of the request in ms. + example: "42" + error_code: + type: string + description: Error code of the request. + example: "0" + error_code_description: + type: string + description: Description of the error code of the request. + example: "Delivered" + currency: + type: string + description: Currency of the price of the request. + example: "EUR" + price: + type: number + description: Price of the request. + example: "0.042" + total_price: + type: number + description: Total price of the request. + example: "0.042" + call_start_date: + type: string + format: date + description: Date of the start of the call. + example: "2019-06-28T15:30:00+0000" + call_end_date: + type: string + format: date + description: Date of the end of the call. + example: "2019-06-28T15:30:00+0000" + duration: + type: integer + description: Duration of the call in seconds. + example: "42" + voice_status_description: + type: string + description: Description of the status of the call. + example: "The call was successful." + date_finalized: + type: string + format: date + description: Date when the request was finalized. + example: "2019-06-28T15:30:00+0000" + first_event_date: + type: string + format: date + description: Date of the first verify event. + example: "2019-06-28T15:30:00+0000" + last_event_date: + type: string + format: date + description: Date of the last verify event. + example: "2019-06-28T15:30:00+0000" + locale: + type: string + description: Locale used for the TTS. + example: "en-gb" + number_type: + type: string + description: Type of phone number to which requests are sent. + example: "MOBILE" + verify_status: + type: string + description: Status of the verify request. + example: "SUCCESS" + sms_event_count: + type: integer + description: Number of sms sent for this verify request. + example: "1" + tts_event_count: + type: integer + description: Number of tts sent for this verify request. + example: "1" + pricing_model: + type: string + description: Pricing model used for this request. + example: "0" + estimated_price: + type: string + description: Estimated price of the verify request. + example: "0.042" + ni_product_type: + type: string + description: Type of Number Insight request. + example: "HLR-LOOKUP" + ni_number: + type: string + description: Number looked up for this request. + example: "HLR-LOOKUP" + ni_status: + type: string + description: Status of the Number Insight request. + example: "Success" + ni_response_code: + type: string + description: Response code of the Number Insight request. + example: "0" + ni_network: + type: string + description: Network of the looked up number. + example: "23415" + ni_network_name: + type: string + description: Network name of the looked up number. + example: "Vodafone Limited" + ni_network_type: + type: string + description: Network type of the looked up number. + example: "mobile" + ni_country: + type: string + description: Country of the looked up number. + example: "mobile" + ni_country_name: + type: string + description: Country name of the looked up number. + example: "mobile" + ni_valid: + type: string + description: Is the looked up number valid. + example: "valid" + ni_ported: + type: string + description: Is the looked up number ported. + example: "ported" + ni_reachable: + type: string + description: Is the looked up number reachable. + example: "reachable" + ni_first_name: + type: string + description: First name of the owner of the looked up number. + example: "valid" + ni_last_name: + type: string + description: Last name of the owner of the looked up number. + example: "valid" + messages_provider: + type: string + description: Provider of the message. + example: "whatsapp" + messages_status: + type: string + description: Status of the message. + example: "submitted" + messages_error_code: + type: string + description: Error code of the message. + example: "1300" + conversations_user_id: + type: string + description: User ID of the caller. + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + ip_voice_leg_id: + type: string + description: Id of the leg. + example: "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" + ip_voice_status: + type: string + description: Status of the conversation evnet. + example: "0" + + report_request: + type: object + properties: + product: + $ref: '#/components/schemas/product' + account_id: + $ref: '#/components/schemas/account_id' + date_start: + $ref: '#/components/schemas/date_start' + date_end: + $ref: '#/components/schemas/date_end' + include_subaccounts: + $ref: '#/components/schemas/include_subaccounts' + callback_url: + $ref: '#/components/schemas/callback_url' + required: + - product + - account_id + discriminator: + propertyName: product + + sms_fields: + type: object + properties: + product: + $ref: '#/components/schemas/product_sms' + direction: + $ref: '#/components/schemas/direction' + status: + $ref: '#/components/schemas/sms_status' + client_ref: + $ref: '#/components/schemas/client_ref' + account_ref: + $ref: '#/components/schemas/account_ref' + include_message: + $ref: '#/components/schemas/include_message' + network: + $ref: '#/components/schemas/network' + from: + $ref: '#/components/schemas/from' + to: + $ref: '#/components/schemas/to' + required: + - product + - account_id + - direction + + voice_call_fields: + type: object + properties: + product: + $ref: '#/components/schemas/product_voice' + direction: + $ref: '#/components/schemas/direction' + network: + $ref: '#/components/schemas/network' + from: + $ref: '#/components/schemas/from' + to: + $ref: '#/components/schemas/to' + status: + $ref: '#/components/schemas/voice_status' + + verify_api_fields: + type: object + properties: + product: + $ref: '#/components/schemas/product_verify' + to: + $ref: '#/components/schemas/to' + network: + $ref: '#/components/schemas/network' + + number_insight_fields: + type: object + properties: + product: + $ref: '#/components/schemas/product_ni' + number: + $ref: '#/components/schemas/number' + network: + $ref: '#/components/schemas/network' + + conversations_fields: + type: object + properties: + product: + $ref: '#/components/schemas/product_conversations' + type: + $ref: '#/components/schemas/conversation_type' + conversation_id: + $ref: '#/components/schemas/conversation_id' + status: + $ref: '#/components/schemas/status' + + messages_fields: + type: object + properties: + product: + $ref: '#/components/schemas/product_messages' + direction: + $ref: '#/components/schemas/direction' + from: + $ref: '#/components/schemas/from' + to: + $ref: '#/components/schemas/to' + uuid: + $ref: '#/components/schemas/uuid' + include_message: + $ref: '#/components/schemas/include_message' + + SMS: + description: SMS + x-tab-id: SMS + allOf: + - $ref: '#/components/schemas/report_request' + - $ref: '#/components/schemas/sms_fields' + + VOICE-CALL: + description: Voice Call + x-tab-id: voice-call + allOf: + - $ref: '#/components/schemas/report_request' + - $ref: '#/components/schemas/voice_call_fields' + + VERIFY-API: + description: Verify + x-tab-id: verify + allOf: + - $ref: '#/components/schemas/report_request' + - $ref: '#/components/schemas/verify_api_fields' + + NUMBER-INSIGHT: + description: Number Insight + x-tab-id: number-insight + allOf: + - $ref: '#/components/schemas/report_request' + - $ref: '#/components/schemas/number_insight_fields' + + CONVERSATIONS: + description: Conversations + x-tab-id: conversations + allOf: + - $ref: '#/components/schemas/report_request' + - $ref: '#/components/schemas/conversations_fields' + + MESSAGES: + description: Messages + x-tab-id: messages + allOf: + - $ref: '#/components/schemas/report_request' + - $ref: '#/components/schemas/messages_fields' + + report_response: + type: object + properties: + request_id: + $ref: '#/components/schemas/request_id' + request_status: + $ref: '#/components/schemas/request_status' + product: + $ref: '#/components/schemas/product' + account_id: + $ref: '#/components/schemas/account_id' + date_start: + $ref: '#/components/schemas/date_start' + date_end: + $ref: '#/components/schemas/date_end' + include_subaccounts: + $ref: '#/components/schemas/include_subaccounts' + callback_url: + $ref: '#/components/schemas/callback_url' + receive_time: + $ref: '#/components/schemas/receive_time' + start_time: + $ref: '#/components/schemas/start_time' + _links: + $ref: '#/components/schemas/_links' + items_count: + $ref: '#/components/schemas/items_count' + + report_response_create_report: + type: object + properties: + request_id: + $ref: '#/components/schemas/request_id' + request_status: + $ref: '#/components/schemas/request_status_create_report' + product: + $ref: '#/components/schemas/product' + account_id: + $ref: '#/components/schemas/account_id' + date_start: + $ref: '#/components/schemas/date_start' + date_end: + $ref: '#/components/schemas/date_end' + include_subaccounts: + $ref: '#/components/schemas/include_subaccounts' + callback_url: + $ref: '#/components/schemas/callback_url' + receive_time: + $ref: '#/components/schemas/receive_time' + start_time: + $ref: '#/components/schemas/start_time' + _links: + $ref: '#/components/schemas/_links_create_report' + + sms_response_get_report: + description: SMS + x-tab-id: SMS + allOf: + - $ref: '#/components/schemas/report_response' + - $ref: '#/components/schemas/sms_fields' + + sms_response_create_report: + description: SMS + x-tab-id: SMS + allOf: + - $ref: '#/components/schemas/report_response_create_report' + - $ref: '#/components/schemas/sms_fields' + + voice_call_response_get_report: + description: Voice Call + x-tab-id: voice-call + allOf: + - $ref: '#/components/schemas/report_response' + - $ref: '#/components/schemas/voice_call_fields' + + voice_call_response_create_report: + description: Voice Call + x-tab-id: voice-call + allOf: + - $ref: '#/components/schemas/report_response_create_report' + - $ref: '#/components/schemas/voice_call_fields' + + number_insight_response_get_report: + description: Number Insight + x-tab-id: number-insight + allOf: + - $ref: '#/components/schemas/report_response' + - $ref: '#/components/schemas/number_insight_fields' + + number_insight_response_create_report: + description: Number Insight + x-tab-id: number-insight + allOf: + - $ref: '#/components/schemas/report_response_create_report' + - $ref: '#/components/schemas/number_insight_fields' + + verify_api_response_get_report: + description: Verify + x-tab-id: verify + allOf: + - $ref: '#/components/schemas/report_response' + - $ref: '#/components/schemas/verify_api_fields' + + verify_api_response_create_report: + description: Verify + x-tab-id: verify + allOf: + - $ref: '#/components/schemas/report_response_create_report' + - $ref: '#/components/schemas/verify_api_fields' + + conversations_response_get_report: + description: Conversations + x-tab-id: conversations + allOf: + - $ref: '#/components/schemas/report_response' + - $ref: '#/components/schemas/conversations_fields' + + conversations_response_create_report: + description: Conversations + x-tab-id: conversations + allOf: + - $ref: '#/components/schemas/report_response_create_report' + - $ref: '#/components/schemas/conversations_fields' + + messages_response_get_report: + description: Messages + x-tab-id: messages + allOf: + - $ref: '#/components/schemas/report_response' + - $ref: '#/components/schemas/messages_fields' + + messages_response_create_report: + description: Messages + x-tab-id: messages + allOf: + - $ref: '#/components/schemas/report_response_create_report' + - $ref: '#/components/schemas/messages_fields' + + csv_sms_outbound: + description: Outbound SMS + x-tab-id: outbound-sms + type: object + properties: + account_id: + $ref: '#/components/schemas/account_id' + message_id: + $ref: '#/components/schemas/message_id' + client_ref: + $ref: '#/components/schemas/client_ref' + direction: + $ref: '#/components/schemas/direction' + from: + $ref: '#/components/schemas/from' + to: + $ref: '#/components/schemas/to' + message_body: + $ref: '#/components/schemas/message_body' + network: + $ref: '#/components/schemas/network' + network_name: + $ref: '#/components/schemas/network_name' + country: + $ref: '#/components/schemas/country' + country_name: + $ref: '#/components/schemas/country_name' + date_received: + $ref: '#/components/schemas/date' + date_finalized: + $ref: '#/components/schemas/date_finalized' + latency: + $ref: '#/components/schemas/latency' + status: + $ref: '#/components/schemas/sms_status' + error_code: + $ref: '#/components/schemas/error_code' + error_code_description: + $ref: '#/components/schemas/error_code_description' + currency: + $ref: '#/components/schemas/currency' + total_price: + $ref: '#/components/schemas/price' + + csv_sms_inbound: + description: Inbound SMS + x-tab-id: inbound-sms + type: object + properties: + account_id: + $ref: '#/components/schemas/account_id' + message_id: + $ref: '#/components/schemas/message_id' + direction: + $ref: '#/components/schemas/direction' + from: + $ref: '#/components/schemas/from' + to: + $ref: '#/components/schemas/to' + message_body: + $ref: '#/components/schemas/message_body' + network: + $ref: '#/components/schemas/network' + network_name: + $ref: '#/components/schemas/network_name' + country: + $ref: '#/components/schemas/country' + country_name: + $ref: '#/components/schemas/country_name' + date_received: + $ref: '#/components/schemas/date' + currency: + $ref: '#/components/schemas/currency' + total_price: + $ref: '#/components/schemas/price' + + csv_voice: + description: Voice Call + x-tab-id: voice-call + type: object + properties: + account_id: + $ref: '#/components/schemas/account_id' + call_id: + $ref: '#/components/schemas/request_id' + direction: + $ref: '#/components/schemas/direction' + from: + $ref: '#/components/schemas/from' + to: + $ref: '#/components/schemas/to' + network: + $ref: '#/components/schemas/network' + network_name: + $ref: '#/components/schemas/network_name' + country: + $ref: '#/components/schemas/country' + country_name: + $ref: '#/components/schemas/country_name' + date_start: + $ref: '#/components/schemas/call_start_date' + date_end: + $ref: '#/components/schemas/call_end_date' + duration: + $ref: '#/components/schemas/duration' + status: + $ref: '#/components/schemas/voice_status' + status_description: + $ref: '#/components/schemas/voice_status_description' + currency: + $ref: '#/components/schemas/currency' + price: + $ref: '#/components/schemas/price' + total_price: + $ref: '#/components/schemas/total_price' + + csv_verify: + description: Verify + x-tab-id: verify + type: object + properties: + account_id: + $ref: '#/components/schemas/account_id' + request_id: + $ref: '#/components/schemas/request_id' + from: + $ref: '#/components/schemas/from' + to: + $ref: '#/components/schemas/to' + locale: + $ref: '#/components/schemas/locale' + number_type: + $ref: '#/components/schemas/number_type' + network: + $ref: '#/components/schemas/network' + network_name: + $ref: '#/components/schemas/network_name' + country: + $ref: '#/components/schemas/country' + country_name: + $ref: '#/components/schemas/country_name' + date_received: + $ref: '#/components/schemas/date' + date_finalized: + $ref: '#/components/schemas/date_finalized' + first_event_date: + $ref: '#/components/schemas/first_event_date' + last_event_date: + $ref: '#/components/schemas/last_event_date' + verify_status: + $ref: '#/components/schemas/verify_status' + sms_event_count: + $ref: '#/components/schemas/sms_event_count' + tts_event_count: + $ref: '#/components/schemas/tts_event_count' + currency: + $ref: '#/components/schemas/currency' + pricing_model: + $ref: '#/components/schemas/pricing_model' + price: + $ref: '#/components/schemas/price' + estimated_price: + $ref: '#/components/schemas/estimated_price' + + csv_ni: + description: Number Insight + x-tab-id: number-insight + type: object + properties: + account_id: + $ref: '#/components/schemas/account_id' + request_id: + $ref: '#/components/schemas/request_id' + product_type: + $ref: '#/components/schemas/ni_product_type' + number: + $ref: '#/components/schemas/ni_number' + date_received: + $ref: '#/components/schemas/date' + status: + $ref: '#/components/schemas/ni_status' + response_code: + $ref: '#/components/schemas/ni_response_code' + network: + $ref: '#/components/schemas/ni_network' + network_name: + $ref: '#/components/schemas/ni_network_name' + network_type: + $ref: '#/components/schemas/ni_network_type' + country: + $ref: '#/components/schemas/ni_country' + country_name: + $ref: '#/components/schemas/ni_country_name' + valid: + $ref: '#/components/schemas/ni_valid' + ported: + $ref: '#/components/schemas/ni_ported' + reachable: + $ref: '#/components/schemas/ni_reachable' + first_name: + $ref: '#/components/schemas/ni_first_name' + last_name: + $ref: '#/components/schemas/ni_last_name' + currency: + $ref: '#/components/schemas/currency' + total_price: + $ref: '#/components/schemas/total_price' + + csv_messages_outbound: + description: Outbound Messages + x-tab-id: outbound-messages + type: object + properties: + account_id: + $ref: '#/components/schemas/account_id' + message_id: + $ref: '#/components/schemas/message_id' + client_ref: + $ref: '#/components/schemas/client_ref' + direction: + $ref: '#/components/schemas/direction' + from: + $ref: '#/components/schemas/from' + to: + $ref: '#/components/schemas/to' + message_body: + $ref: '#/components/schemas/message_body' + country: + $ref: '#/components/schemas/country' + country_name: + $ref: '#/components/schemas/country_name' + provider: + $ref: '#/components/schemas/messages_provider' + date_received: + $ref: '#/components/schemas/date' + date_finalized: + $ref: '#/components/schemas/date_finalized' + latency: + $ref: '#/components/schemas/latency' + status: + $ref: '#/components/schemas/messages_status' + error_code: + $ref: '#/components/schemas/messages_error_code' + currency: + $ref: '#/components/schemas/currency' + total_price: + $ref: '#/components/schemas/total_price' + + csv_messages_inbound: + description: Inbound Messages + x-tab-id: inbound-messages + type: object + properties: + account_id: + $ref: '#/components/schemas/account_id' + message_id: + $ref: '#/components/schemas/message_id' + direction: + $ref: '#/components/schemas/direction' + from: + $ref: '#/components/schemas/from' + to: + $ref: '#/components/schemas/to' + message_body: + $ref: '#/components/schemas/message_body' + provider: + $ref: '#/components/schemas/messages_provider' + date_received: + $ref: '#/components/schemas/date' + currency: + $ref: '#/components/schemas/currency' + total_price: + $ref: '#/components/schemas/total_price' + + csv_conversations_ip_voice: + description: IP Voice Conversations + x-tab-id: conversations-ip-voice + type: object + properties: + account_id: + $ref: '#/components/schemas/account_id' + request_id: + $ref: '#/components/schemas/request_id' + application_id: + $ref: '#/components/schemas/application_id' + conversation_id: + $ref: '#/components/schemas/conversation_id' + user_id: + $ref: '#/components/schemas/conversations_user_id' + leg_id: + $ref: '#/components/schemas/ip_voice_leg_id' + date_start: + $ref: '#/components/schemas/call_start_date' + date_end: + $ref: '#/components/schemas/call_end_date' + duration: + $ref: '#/components/schemas/duration' + status: + $ref: '#/components/schemas/ip_voice_status' + currency: + $ref: '#/components/schemas/currency' + price: + $ref: '#/components/schemas/price' + total_price: + $ref: '#/components/schemas/total_price' + +x-errors: + invalid-request-parameters: + description: The provided payload is invalid + resolution: Modify your request to provide a valid payload + link: + text: View API reference + url: /api/reports#create-report diff --git a/tests/resources/definitions/sms.yml b/tests/resources/definitions/sms.yml new file mode 100755 index 00000000..21b34a8d --- /dev/null +++ b/tests/resources/definitions/sms.yml @@ -0,0 +1,487 @@ +openapi: "3.0.0" +info: + version: 1.0.5 + title: SMS API + description: With the Nexmo SMS API you can send SMS from your account and lookup messages both messages that you've sent as well as messages sent to your virtual numbers. Numbers are specified in E.164 format. More SMS API documentation is at + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' +servers: + - url: https://rest.nexmo.com/sms +paths: + /{format}: + post: + operationId: send-an-sms + summary: Send an SMS + description: Send an outbound SMS from your Nexmo account + x-code-example-path: messaging.sms.send + parameters: + - name: format + description: The format of the response + in: path + required: true + schema: + example: json + type: string + enum: + - json + - xml + default: json + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/NewMessage' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/SMS' + text/xml: + schema: + $ref: '#/components/schemas/MessageXmlWrapper' + + callbacks: + delivery-receipt: + '{$request.body#/callback}': + post: + summary: Delivery Receipt + operationId: delivery-receipt + x-example-path: '/webhooks/delivery-receipt' + description: The following are parameters sent in as a [delivery receipt](https://developer.nexmo.com/messaging/sms/guides/delivery-receipts) callback. You can subscribe to [webhooks](https://developer.nexmo.com/concepts/guides/webhooks) to receive notification when the delivery status of an SMS that you have sent with Nexmo changes. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/DeliveryReceipt' + responses: + '200': + description: Your server returns this code if it accepts the callback +x-webhooks: + inbound-sms: + '{$request.body#/callback}': + post: + summary: Inbound SMS + operationId: inbound-sms + x-example-path: '/webhooks/inbound-sms' + description: | + If you rent one or more virtual numbers from Nexmo, inbound messages to that number are sent to your [webhook endpoint](https://developer.nexmo.com/concepts/guides/webhooks). + + When you receive an inbound message, you must send a 2xx response. If you do not send a 2xx response Nexmo will resend the inbound message for the next 24 hours. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/InboundMessage' + responses: + '200': + description: Your server returns this code if it accepts the callback + +components: + schemas: + NewMessage: + required: + - api_key + - from + - to + properties: + api_key: + description: Your API key + type: string + minLength: 8 + maxLength: 8 + example: abcd1234 + api_secret: + description: Your API secret. Required unless `sig` is provided + example: abcdef0123456789 + type: string + minLength: 16 + maxLength: 16 + sig: + description: The hash of the request parameters in alphabetical order, a timestamp and the signature secret. See [Signing Requests](/concepts/guides/signing-messages) for more details. + type: string + minLength: 16 + maxLength: 16 + from: + description: The name or number the message should be sent from. Alphanumeric senderID's are not supported in all countries, see [Global Messaging](https://developer.nexmo.com/messaging/sms/guides/global-messaging#country-specific-features) for more details. If alphanumeric, spaces will be ignored. Numbers are specified in E.164 format. + type: string + example: 'AcmeInc' + to: + description: The number that the message should be sent to. Numbers are specified in E.164 format. + type: string + minLength: 7 + maxLength: 15 + pattern: '\d{7,15}' + example: 447700900000 + text: + description: The body of the message being sent. If your message contains characters that can be encoded according to the GSM Standard and Extended tables then you can set the `type` to `text`. If your message contains characters outside this range, then you will need to set the `type` to `unicode`. + type: string + example: Hello World! + ttl: + description: '**Advanced**: The duration in milliseconds the delivery of an SMS will be attempted.§§ By default Nexmo attempt delivery for 72 hours, however the maximum effective value depends on the operator and is typically 24 - 48 hours. We recommend this value should be kept at its default or at least 30 minutes.' + type: integer + example: 900000 + default: 259200000 + minimum: 20000 + maximum: 604800000 + status-report-req: + description: '**Advanced**: Boolean indicating if you like to receive a [Delivery Receipt](https://developer.nexmo.com/messaging/sms/building-blocks/receive-a-delivery-receipt).' + type: boolean + example: false + default: true + callback: + description: '**Advanced**: The webhook endpoint the delivery receipt for this sms is sent to. This parameter overrides the webhook endpoint you set in Dashboard.' + type: string + example: 'https://example.com/sms-dlr' + message-class: + description: '**Advanced**: The Data Coding Scheme value of the message' + type: integer + enum: + - 0 + - 1 + - 2 + - 3 + type: + description: '**Advanced**: The format of the message body' + type: string + enum: + - text + - binary + - wappush + - unicode + - vcal + - vcard + example: text + default: text + vcard: + description: '**Advanced**: A business card in [vCard format](https://en.wikipedia.org/wiki/VCard). Depends on `type` parameter having the value `vcard`.' + type: string + format: vcard + vcal: + description: '**Advanced**: A calendar event in [vCal format](https://en.wikipedia.org/wiki/VCal). Depends on `type` parameter having the value `vcal`.' + type: string + format: vcal + body: + description: '**Advanced**: Hex encoded binary data. Depends on `type` parameter having the value `binary`.' + type: string + example: 0011223344556677 + udh: + description: '**Advanced**: Your custom Hex encoded [User Data Header](https://en.wikipedia.org/wiki/User_Data_Header). Depends on `type` parameter having the value `binary`.' + type: string + example: 06050415811581 + protocol-id: + description: '**Advanced**: The value of the [protocol identifier](https://en.wikipedia.org/wiki/GSM_03.40#Protocol_Identifier) to use. Ensure that the value is aligned with `udh`.' + type: integer + example: 127 + title: + description: '**Advanced**: The title for a wappush SMS. Depends on `type` parameter having the value `wappush`.' + type: string + example: Welcome + url: + description: '**Advanced**: The URL of your website. Depends on `type` parameter having the value `wappush`.' + type: string + example: https://example.com + validity: + description: '**Advanced**: The availability for an SMS in milliseconds. Depends on `type` parameter having the value `wappush`.' + type: string + example: 300000 + client-ref: + description: '**Advanced**: You can optionally include your own reference of up to 40 characters.' + type: string + example: my-personal-reference + account-ref: + description: '**Advanced**: An optional string used to identify separate accounts using the SMS endpoint for billing purposes. To use this feature, please email [support@nexmo.com](mailto:support@nexmo.com)' + type: string + example: customer1234 + Error: + type: object + properties: + message-count: + type: string + description: The amount of messages in the request + example: 1 + messages: + type: array + items: + $ref: '#/components/schemas/ErrorMessage' + ErrorMessage: + type: object + properties: + status: + type: string + description: The error status of the message + example: '2' + error-text: + type: string + description: The description of the error + example: 'Missing to param' + SMS: + type: object + properties: + message-count: + type: string + description: The amount of messages in the request + example: 1 + messages: + type: array + items: + $ref: '#/components/schemas/Message' + InboundMessage: + type: object + required: + - msisdn + - to + - messageId + - text + - type + - keyword + - message-timestamp + properties: + msisdn: + type: string + description: The phone number that this inbound message was sent from. Numbers are specified in E.164 format. + example: '447700900001' + to: + type: string + description: The phone number the message was sent to. **This is your virtual number**. Numbers are specified in E.164 format. + example: '447700900000' + messageId: + type: string + description: The ID of the message + example: 0A0000000123ABCD1 + text: + type: string + description: The message body for this inbound message. + example: Hello world + type: + type: string + description: | + Possible values are: + + - `text` - standard text. + - `unicode` - URLencoded unicode . This is valid for standard GSM, Arabic, Chinese, double-encoded characters and so on. + - `binary` - a binary message. + example: 'text' + keyword: + type: string + description: The first word in the message body. Converted to upper case. + example: HELLO + message-timestamp: + description: The time when Nexmo started to push this Delivery Receipt to your webhook endpoint. + type: string + example: 2020-01-01 12:00:00 + timestamp: + description: A unix timestamp representation of message-timestamp. + type: string + example: "1578787200" + nonce: + type: string + description: A random string that forms part of the signed set of parameters, it adds an extra element of unpredictability into the signature for the request. You use the nonce and timestamp parameters with your shared secret to calculate and validate the signature for inbound messages. + example: aaaaaaaa-bbbb-cccc-dddd-0123456789ab + concat: + type: string + description: True - if this is a concatenated message. This field does not exist if it is a single message + example: 'true' + concat-ref: + type: string + description: The transaction reference. All parts of this message share this value. + example: '1' + concat-total: + type: string + description: The number of parts in this concatenated message. + example: '3' + concat-part: + type: string + description: The number of this part in the message. Counting starts at 1. + example: '2' + data: + type: string + format: binary + description: The content of this message, if type is binary. + udh: + type: string + description: The hex encoded User Data Header, if type is binary + Message: + type: object + properties: + to: + type: string + description: The number the message was sent to. Numbers are specified in E.164 format. + example: '447700900000' + message-id: + type: string + description: The ID of the message + example: 0A0000000123ABCD1 + xml: + name: messageId + status: + type: string + description: The status of the message. See [Troubleshooting Failed SMS](https://developer.nexmo.com/messaging/sms/guides/troubleshooting-sms). + example: '0' + remaining-balance: + type: string + description: Your remaining balance + example: '3.14159265' + xml: + name: remainingBalance + message-price: + type: string + description: The cost of the message + example: '0.03330000' + xml: + name: messagePrice + network: + type: string + description: The ID of the network of the recipient + example: '12345' + account-ref: + type: string + description: '**Advanced**: An optional string used to identify separate accounts using the SMS endpoint for billing purposes. To use this feature, please email [support@nexmo.com](mailto:support@nexmo.com)' + example: customer1234 + MessageXmlWrapper: + type: object + xml: + name: mt-submission-response + properties: + messages: + x-skip-response-description: true + type: array + items: + $ref: '#/components/schemas/Message' + properties: + count: + type: integer + example: 1 + xml: + attribute: true + DeliveryReceipt: + type: object + properties: + msisdn: + type: string + description: The number the message was sent to. Numbers are specified in E.164 format. + example: '447700900000' + to: + type: string + description: The SenderID you set in `from` in your request. + example: AcmeInc + network-code: + type: string + description: The Mobile Country Code Mobile Network Code (MCCMNC) of the carrier this phone number is registered with. + example: '12345' + messageId: + type: string + description: The Nexmo ID for this message. + example: '0A0000001234567B' + price: + type: string + description: The cost of the message + example: '0.03330000' + status: + type: string + description: A code that explains where the message is in the delivery process. + example: 'delivered' + x-possible-values: + - delivered + - expired + - failed + - rejected + - accepted + - buffered + - unknown + scts: + type: string + description: When the DLR was recieved from the carrier in the following format `YYMMDDHHMM`. For example, `2001011400` is at `2020-01-01 14:00` + example: '2001011400' + err-code: + type: string + description: The status of the request. Will be a non `0` value if there has been an error. See the [Delivery Receipt documentation](https://developer.nexmo.com/messaging/sms/guides/delivery-receipts#dlr-error-codes) for more details + example: '0' + message-timestamp: + description: The time when Nexmo started to push this Delivery Receipt to your webhook endpoint. + type: string + example: 2020-01-01 12:00:00 + +x-errors: + "1": + description: Throttled. You are sending SMS faster than the account limit. + resolution: Refer to [What is the Throughput Limit for Outbound SMS?](https://help.nexmo.com/hc/en-us/articles/203993598) for more information. + + "2": + description: Missing Parameters. Your request is missing one of the required parameters `from`, `to`, `api_key`, `api_secret` or `text`. + resolution: Check your parameters and try again. + + "3": + description: Invalid Parameters. The value of one or more parameters is invalid. + resolution: Check your parameters and try again. + + "4": + description: Invalid Credentials. Your API key and/or secret are incorrect, invalid or disabled. + resolution: Visit the [Dashboard](https://dashboard.nexmo.com) and check your credentials. + + "5": + description: Internal Error. An error has occurred in the platform whilst processing this message. + resolution: If the error persists, contact support@nexmo.com. + + "6": + description: Invalid Message. The platform was unable to process this message, for example, an un-recognized number prefix. + resolution: N/A + + "7": + description: Number Barred. The number you are trying to send messages to is blacklisted and may not receive them. + resolution: N/A + + "8": + description: Partner Account Barred. Your Nexmo account has been suspended. + resolution: Contact . + + "9": + description: Partner Quota Violation. You do not have sufficient credit to send the message. + resolution: Top-up and retry. + + "10": + description: Too Many Existing Binds. The number of simultaneous connections to the platform exceeds your account allocation. + resolution: Back-off and retry. + + "11": + description: Account Not Enabled For HTTP. This account is not provisioned for the SMS API. + resolution: This error usually indicates that you should use SMPP instead. + + "12": + description: Message Too Long. The message length exceeds the maximum allowed. + resolution: Send shorter messages. + + "14": + description: Invalid Signature. The signature supplied could not be verified. + resolution: Check the [documentation for signing messages](https://developer.nexmo.com/concepts/guides/signing-messages) or use one of the [SDKs](https://developer.nexmo.com/tools) to handle the signing. + + "15": + description: Invalid Sender Address. You are using a non-authorized sender ID in the `from` field. + resolution: This is most commonly seen in North America, where a Nexmo long virtual number or short code is required. + + + "22": + description: Invalid Network Code. The network code supplied was either not recognized, or does not match the country of the destination address. + resolution: Check the network code or remove it from your request. + + "23": + description: Invalid Callback Url. The callback URL supplied was either too long or contained illegal characters. + resolution: Supply a valid URL for the callback. + + "29": + description: Non-Whitelisted Destination. Your Nexmo account is still in demo mode. While in demo mode you must add target numbers to your whitelisted destination list. + resolution: Top-up your account to remove this limitation. + + "32": + description: Signature And API Secret Disallowed. A signed request may not also present an `api_secret`. + resolution: Remove the API secret from your request, or don't sign the message. + + "33": + description: Number De-activated. The number you are trying to send messages to is de-activated and may not receive them. + resolution: N/A diff --git a/tests/resources/definitions/subaccounts.yml b/tests/resources/definitions/subaccounts.yml new file mode 100755 index 00000000..111b38f7 --- /dev/null +++ b/tests/resources/definitions/subaccounts.yml @@ -0,0 +1,665 @@ +openapi: 3.0.0 +servers: + - url: 'https://api.nexmo.com/accounts' +info: + version: 1.0.5 + title: Subaccounts API + description: >- + The Nexmo Subaccounts API enables you to create subaccounts under your primary account. Subaccounts facilitate differential product configuration, reporting, and billing. The Subaccounts API is released initially with restricted availability. You can read more about the API in the [Subaccounts documentation](/account/subaccounts/overview). + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' +paths: + /{api_key}/subaccounts: + post: + summary: Create subaccount + operationId: createSubAccount + description: > + Create a subaccount for a given primary account. + tags: + - Subaccount Management + security: + - basicAuth: [] + parameters: + - name: api_key + in: path + description: ID of the primary account + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/NewSubaccountRequest' + responses: + '200': + description: Subaccount response + content: + application/json: + schema: + $ref: '#/components/schemas/SubaccountCreateResponse' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' + '422': + $ref: '#/components/responses/UnprocessableEntityError' + get: + summary: Retrieve list of subaccounts + operationId: retrieveSubaccountsList + description: > + Get the information of all the subaccounts owned by the primary account. + tags: + - Subaccount Management + security: + - basicAuth: [] + parameters: + - name: api_key + in: path + description: ID of the primary account + required: true + schema: + type: string + responses: + '200': + description: Subaccounts response + content: + application/json: + schema: + $ref: '#/components/schemas/SubaccountsAllResponse' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' + /{api_key}/subaccounts/{subaccount_key}: + get: + summary: Retrieve a subaccount + operationId: retrieveSubaccount + description: > + Get the information of a subaccount specified with its API key. + tags: + - Subaccount Management + security: + - basicAuth: [] + parameters: + - name: api_key + in: path + description: ID of the primary account + required: true + schema: + type: string + - name: subaccount_key + in: path + description: ID of the subaccount + required: true + schema: + type: string + responses: + '200': + description: Subaccount response + content: + application/json: + schema: + $ref: '#/components/schemas/SubaccountResponse' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' + patch: + summary: Modify a subaccount + operationId: modifySubaccount + description: > + Change one or more properties of a subaccount. + tags: + - Subaccount Management + security: + - basicAuth: [] + parameters: + - name: api_key + in: path + description: ID of the primary account + required: true + schema: + type: string + - name: subaccount_key + in: path + description: ID of the subaccount + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ModifySubaccountRequest' + responses: + '200': + description: Subaccount response + content: + application/json: + schema: + $ref: '#/components/schemas/SubaccountResponse' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' + '422': + $ref: '#/components/responses/UnprocessableEntityError' + /{api_key}/credit-transfers: + post: + summary: Transfer credit + operationId: transferCredit + description: > + Transfer credit limit between a primary account and one of its subaccounts. + tags: + - Transfers + security: + - basicAuth: [] + parameters: + - name: api_key + in: path + description: ID of the primary account + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/TransferBalanceOrCreditRequest' + responses: + '200': + description: Credit transfer response + content: + application/json: + schema: + $ref: '#/components/schemas/TransferCreditResponse' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' + '422': + $ref: '#/components/responses/UnprocessableEntityError' + get: + summary: Retrieve list of credit transfers + operationId: retrieveCreditTransfers + description: > + Retrieve a list of credit transfers that have taken place for a primary account within a specified time period. + tags: + - Transfers + security: + - basicAuth: [] + parameters: + - name: api_key + in: path + description: ID of the primary account. + required: true + schema: + type: string + - name: start_date + in: query + description: Start of the retrieval period. + required: true + schema: + type: string + example: '2019-07-15T13:11:44Z' + - name: end_date + in: query + description: End of the retrieval period. If absent then all transfers until now is returned. + required: false + schema: + type: string + example: '2019-07-15T14:11:44Z' + - name: subaccount + in: query + description: Subaccount to filter by. You may send this multiple times to filter on multiple subaccounts + required: false + schema: + type: string + responses: + '200': + description: List credit transfers response + content: + application/json: + schema: + $ref: '#/components/schemas/ListCreditTransfersResponse' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' + /{api_key}/balance-transfers: + post: + summary: Transfer balance + description: > + Transfer balance between a primary account and one of its subaccounts. + Note that balance_available_for_transfer = |account_balance - credit_limit| of the source account. + tags: + - Transfers + security: + - basicAuth: [] + operationId: transferBalance + parameters: + - name: api_key + in: path + description: ID of the primary account + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/TransferBalanceOrCreditRequest' + responses: + '200': + description: Balance transfer response + content: + application/json: + schema: + $ref: '#/components/schemas/TransferBalanceResponse' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' + '422': + $ref: '#/components/responses/UnprocessableEntityError' + get: + summary: Retrieve list of balance transfers + operationId: retrieveBalanceTransfers + description: > + Retrieve a list of balance transfers that have taken place for a primary account within a specified time period. + tags: + - Transfers + security: + - basicAuth: [] + parameters: + - name: api_key + in: path + description: ID of the primary account. + required: true + schema: + type: string + - name: start_date + in: query + description: Start of the retrieval period. + required: true + schema: + type: string + example: '2019-07-15T13:11:44Z' + - name: end_date + in: query + description: End of the retrieval period. If absent then all transfers until now is returned. + required: false + schema: + type: string + example: '2019-07-15T14:11:44Z' + - name: subaccount + in: query + description: Subaccount to filter by. You may send this multiple times to filter on multiple subaccounts + required: false + schema: + type: string + responses: + '200': + description: List balance transfers response + content: + application/json: + schema: + $ref: '#/components/schemas/ListBalanceTransfersResponse' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' +tags: + - name: Subaccount Management + description: This section shows how you can create, retrieve and modify subaccounts of a primary account. + - name: Transfers + description: This section shows how you execute credit and balance transfers, as well as viewing past transactions. +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + responses: + UnauthorizedError: + description: Credential is missing or invalid + content: + application/json: + schema: + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#unauthorized' + title: + type: string + example: 'Invalid credentials supplied' + detail: + type: string + example: 'You did not provide correct credentials' + instance: + type: string + example: '798b8f199c45014ab7b08bfe9cc1c12c' + ForbiddenError: + description: Action is forbidden + content: + application/json: + schema: + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#unprovisioned' + title: + type: string + example: 'Authorisation error' + detail: + type: string + example: 'Account acc6111f is not provisioned to access Subaccount Provisioning API' + instance: + type: string + example: '158b8f199c45014ab7b08bfe9cc1c12c' + NotFoundError: + description: The account ID provided does not exist in our system or you do not have access + content: + application/json: + schema: + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#invalid-api-key' + title: + type: string + example: 'Invalid API Key' + detail: + type: string + example: "API key 'acc6111f' does not exist, or you do not have access" + instance: + type: string + example: '158b8f199c45014ab7b08bfe9cc1c12c' + UnprocessableEntityError: + description: Validation Error + content: + application/json: + schema: + type: object + required: + - type + - title + - detail + - invalid_parameters + - instance + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors/account/subaccounts#validation' + title: + type: string + example: 'Bad Request' + detail: + type: string + example: 'The request failed due to validation errors' + invalid_parameters: + type: array + items: + type: object + properties: + name: + type: string + example: 'use_primary_account_balance' + reason: + type: string + example: "Should be either 'true' or 'false'" + instance: + type: string + example: '158b8f199c45014ab7b08bfe9cc1c12c' + InternalError: + description: An internal error has occurred + content: + application/json: + schema: + type: object + required: + - type + - title + - detail + - instance + properties: + type: + type: string + example: 'https://developer.nexmo.com/api-errors#internal-error' + title: + type: string + example: 'Internal Error' + detail: + type: string + example: 'An internal error occurred. Please try again shortly' + instance: + type: string + example: '158b8f199c45014ab7b08bfe9cc1c12c' + schemas: + SubaccountsAllResponse: + properties: + _embedded: + type: object + properties: + primary_account: + $ref: '#/components/schemas/SubaccountResponse' + subaccounts: + type: array + items: + $ref: '#/components/schemas/SubaccountResponse' + SubaccountResponse: + properties: + api_key: + example: 'bbe6222f' + type: string + description: >- + Unique subaccount ID. + name: + type: string + example: 'Subaccount department A' + description: >- + Name of the subaccount. + primary_account_api_key: + example: 'acc6111f' + type: string + description: >- + Unique primary account ID. + use_primary_account_balance: + example: true + type: boolean + description: >- + Flag showing if balance is shared with primary account. + created_at: + example: '2018-03-02T16:34:49Z' + type: string + description: >- + Subaccount creation date and time. + suspended: + example: false + type: boolean + description: >- + Subaccount suspension status. + balance: + example: 100.25 + type: number + description: >- + Balance of the subAccount. Value is null if balance is shared with primary account. + credit_limit: + example: -100.25 + type: number + description: >- + Credit limit of the subAccount. Value is null if balance is shared with primary account. + SubaccountCreateResponse: + allOf: + - properties: + secret: + type: string + example: 'Password123' + description: >- + API secret of the subaccount. + - $ref: '#/components/schemas/SubaccountResponse' + TransferCreditResponse: + properties: + credit_transfer_id: + example: '07b5-46e1-a527-85530e625800' + type: string + description: >- + Unique credit transfer ID + amount: + example: 123.45 + type: number + description: >- + Credit transfer amount + from: + example: '7c9738e6' + type: string + description: >- + Account the credit is transferred from + to: + example: 'ad6dc56f' + type: string + description: >- + Account the credit is transferred to + reference: + example: 'This gets added to the audit log' + type: string + description: >- + Reference for the credit transfer + created_at: + example: '2019-03-02T16:34:49Z' + type: string + description: >- + The date and time when the credit transfer was executed + TransferBalanceResponse: + properties: + balance_transfer_id: + example: '07b5-46e1-a527-85530e625800' + type: string + description: >- + Unique balance transfer ID + amount: + example: 123.45 + type: number + description: >- + Balance transfer amount + from: + example: '7c9738e6' + type: string + description: >- + Account the balance is transferred from + to: + example: 'ad6dc56f' + type: string + description: >- + Account the balance is transferred to + reference: + example: 'This gets added to the audit log' + type: string + description: >- + Reference for the balance transfer + created_at: + example: '2019-03-02T16:34:49Z' + type: string + description: >- + The date and time when the balance transfer was executed + ListCreditTransfersResponse: + properties: + _embedded: + type: object + properties: + credit-transfers: + type: array + items: + $ref: '#/components/schemas/TransferCreditResponse' + ListBalanceTransfersResponse: + properties: + _embedded: + type: object + properties: + balance_transfers: + type: array + items: + $ref: '#/components/schemas/TransferBalanceResponse' + NewSubaccountRequest: + type: object + required: + - name + properties: + name: + type: string + example: 'Subaccount department A' + secret: + type: string + example: 'Password123' + use_primary_account_balance: + type: boolean + example: false + default: true + ModifySubaccountRequest: + type: object + properties: + suspended: + type: boolean + example: true + use_primary_account_balance: + type: boolean + example: false + TransferBalanceOrCreditRequest: + type: object + properties: + from: + type: string + example: '7c9738e6' + to: + type: string + example: 'ad6dc56f' + amount: + type: number + example: '123.45' + reference: + type: string + example: 'This gets added to the audit log' + required: + - from + - to + - amount diff --git a/tests/resources/definitions/verify.yml b/tests/resources/definitions/verify.yml new file mode 100755 index 00000000..5d400734 --- /dev/null +++ b/tests/resources/definitions/verify.yml @@ -0,0 +1,810 @@ +openapi: 3.0.0 +servers: + - url: "https://api.nexmo.com/verify" +info: + title: Nexmo Verify API + version: 1.0.10 + description: >- + The Verify API helps you to implement 2FA (two-factor authentication) in your applications. + This is useful for: + + + * Protecting against spam, by preventing spammers from creating multiple accounts + + * Monitoring suspicious activity, by forcing an account user to verify ownership of a number + + * Ensuring that you can reach your users at any time because you have their correct phone number + + More information is available at + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: "https://developer.nexmo.com/" + termsOfService: "https://www.nexmo.com/terms-of-use" + license: + name: The MIT License (MIT) + url: "https://opensource.org/licenses/MIT" +externalDocs: + description: "More information on the Verify product on our Developer Portal" + url: "https://developer.nexmo.com/verify" +security: + - apiKey: [] +paths: + "/{format}": + get: + operationId: verifyRequest + summary: Verify Request + description: >- + Use Verify request to generate and send a PIN to your user: + + + 1. Create a request to send a verification code to your user. + + + 2. Check the `status` field in the response to ensure that your request + was successful (zero is success). + + + 3. Use the `request_id` field in the response for the Verify check. + + parameters: + - $ref: "#/components/parameters/format" + - $ref: "#/components/parameters/api_secret" + - name: number + required: true + in: query + description: >- + The mobile or landline phone number to verify. Unless you are + setting `country` explicitly, this number must be in + [E.164](https://en.wikipedia.org/wiki/E.164) format. + schema: + type: string + example: "447700900000" + - name: country + required: false + in: query + description: >- + If you do not provide `number` in international format or you are not + sure if `number` is correctly formatted, specify the two-character country + code in `country`. Verify will then format the number for you. + schema: + type: string + example: GB + - name: brand + required: true + in: query + description: >- + An 18-character alphanumeric string you can use to personalize the verification + request SMS body, to help users identify your company or application name. + For example: "Your `Acme Inc` PIN is ..." + schema: + type: string + maxLength: 18 + example: Acme Inc + - name: sender_id + required: false + in: query + description: >- + An 11-character alphanumeric string that represents the [identity of the sender](https://developer.nexmo.com/messaging/sms/guides/custom-sender-id) + of the verification request. Depending on the destination of the phone number you are sending the verification SMS to, + restrictions might apply. + schema: + type: string + maxLength: 11 + default: VERIFY + example: ACME + - name: code_length + required: false + in: query + description: The length of the verification code. + schema: + type: integer + enum: + - 4 + - 6 + default: 4 + example: 6 + - name: lg + required: false + in: query + description: >- + By default, the SMS or text-to-speech (TTS) message is generated in + the locale that matches the `number`. For example, the text message + or TTS message for a `33*` number is sent in French. Use this + parameter to explicitly control the language used for the Verify + request. A list of languages is available: + example: en-us + schema: + type: string + default: en-us + enum: + - ar-xa + - cs-cz + - cy-cy + - cy-gb + - da-dk + - de-de + - el-gr + - en-au + - en-gb + - en-in + - en-us + - es-es + - es-mx + - es-us + - fi-fi + - fil-ph + - fr-ca + - fr-fr + - hi-in + - hu-hu + - id-id + - is-is + - it-it + - ja-jp + - ko-kr + - nb-no + - nl-nl + - pl-pl + - pt-br + - pt-pt + - ro-ro + - ru-ru + - sv-se + - tr-tr + - vi-vn + - zh-cn + - zh-tw + - name: pin_expiry + required: false + in: query + description: >- + How long the generated verification code is valid for, in seconds. + When you specify both `pin_expiry` and `next_event_wait` then + `pin_expiry` must be an integer multiple of `next_event_wait` + otherwise `pin_expiry` is defaulted to equal next_event_wait. See + [changing the event + timings](https://developer.nexmo.com/verify/guides/changing-default-timings). + schema: + type: integer + minimum: 60 + maximum: 3600 + default: 300 + example: 240 + - name: next_event_wait + required: false + in: query + description: >- + Specifies the wait time in seconds between attempts to deliver the + verification code. + schema: + type: integer + minimum: 60 + maximum: 900 + default: 300 + example: 120 + - name: workflow_id + required: false + in: query + description: >- + Selects the predefined sequence of SMS and TTS (Text To Speech) + actions to use in order to convey the PIN to your user. For example, + an id of 1 identifies the workflow SMS - TTS - TTS. For a list of + all workflows and their associated ids, please visit the [developer + portal](https://developer.nexmo.com/verify/guides/workflows-and-events). + schema: + type: integer + default: 1 + enum: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + example: 4 + responses: + "200": + description: OK + content: + application/json: + schema: + oneOf: + - $ref: "#/components/schemas/requestResponse" + - $ref: "#/components/schemas/requestErrorResponse" + text/xml: + schema: + oneOf: + - $ref: "#/components/schemas/requestResponse" + - $ref: "#/components/schemas/requestErrorResponse" + "/check/{format}": + get: + description: >- + Use Verify check to confirm that the PIN you received from your user matches the one sent by + Nexmo in your Verify request. + + + 1. Send the verification `code` that your user supplied, with the corresponding `request_id` from the Verify request. + + 2. Check the `status` of the response to determine if the code the user supplied matches the one sent by Nexmo. + operationId: verifyCheck + summary: Verify Check + parameters: + - $ref: "#/components/parameters/format" + - $ref: "#/components/parameters/api_secret" + - name: request_id + required: true + in: query + description: >- + The Verify request to check. This is the + `request_id` you received in the response to the Verify request. + schema: + type: string + maxLength: 32 + example: abcdef012345... + - name: code + required: true + in: query + description: The verification code entered by your user. + schema: + type: string + minLength: 4 + maxLength: 6 + example: "1234" + - name: ip_address + required: false + in: query + description: >- + (This field is no longer used) + schema: + type: string + example: 123.0.0.255 + responses: + "200": + description: OK + content: + application/json: + schema: + oneOf: + - $ref: "#/components/schemas/checkResponse" + - $ref: "#/components/schemas/checkErrorResponse" + text/xml: + schema: + oneOf: + - $ref: "#/components/schemas/checkResponse" + - $ref: "#/components/schemas/checkErrorResponse" + "/search/{format}": + get: + description: >- + Use Verify search to check the status of past or current verification requests: + + + 1. Send a Verify search request containing the `request_id`s of the + verification requests you are interested in. + + 2. Use the `status` of each verification request in the `checks` array of the response object to determine the outcome. + operationId: verifySearch + summary: Verify Search + parameters: + - $ref: "#/components/parameters/format" + - $ref: "#/components/parameters/api_secret" + - name: request_id + required: false + in: query + description: The `request_id` you received in the Verify Request Response. + schema: + type: string + example: abcdef012345... + - name: request_ids + required: false + in: query + description: >- + More than one `request_id`. Each `request_id` is a new parameter in + the Verify Search request. + schema: + type: array + items: + type: string + example: abcdef012345... + maxItems: 10 + style: form + explode: true + responses: + "200": + description: OK + content: + application/json: + schema: + oneOf: + - $ref: "#/components/schemas/searchResponse" + - $ref: "#/components/schemas/searchErrorResponse" + text/xml: + schema: + oneOf: + - $ref: "#/components/schemas/searchResponse" + - $ref: "#/components/schemas/searchErrorResponse" + "/control/{format}": + get: + description: |- + Control the progress of your Verify requests. To cancel an existing Verify request, or to trigger the next verification event: + + + 1. Send a Verify control request with the appropriate command (`cmd`) for what you want to achieve. + + 2. Check the `status` in the response. + operationId: verifyControl + summary: Verify Control + parameters: + - $ref: "#/components/parameters/format" + - $ref: "#/components/parameters/api_secret" + - name: request_id + required: true + in: query + description: "The `request_id` you received in the response to the Verify request." + schema: + type: string + example: abcdef012345... + - name: cmd + required: true + in: query + description: >- + The command to execute, depending on whether you want to cancel + the verification process, or advance to the next verification event. + Cancellation is only possible 30 seconds after the start of the + verification request and before the second event (either TTS or SMS) + has taken place. + schema: + type: string + enum: + - cancel + - trigger_next_event + x-ms-enum: + values: + - value: cancel + description: stop the request + - value: trigger_next_event + description: advance the request to the next part of the process. + example: cancel + responses: + "200": + description: OK + content: + application/json: + schema: + oneOf: + - $ref: "#/components/schemas/controlResponse" + - $ref: "#/components/schemas/controlErrorResponse" + text/xml: + schema: + oneOf: + - $ref: "#/components/schemas/controlResponse" + - $ref: "#/components/schemas/controlErrorResponse" +components: + parameters: + format: + name: format + required: true + in: path + description: The response format. + schema: + type: string + enum: + - json + - xml + example: json + api_secret: + name: api_secret + required: true + in: query + description: >- + You can find your API secret in your Nexmo account [developer + dashboard](https://dashboard.nexmo.com) + schema: + type: string + schemas: + estimated_price_messages_sent: + type: string + description: | + This field may not be present, depending on your pricing model. The + value indicates the cost (in EUR) of the calls made and messages sent + for the verification process. This value may be updated during and + shortly after the request completes because user input events can + overlap with message/call events. When this field is present, the total + cost of the verification is the sum of this field and the `price` field. + example: "0.03330000" + requestResponse: + type: object + description: Success + xml: + name: verify_response + properties: + request_id: + type: string + description: >- + The unique ID of the Verify request. You need this + `request_id` for the Verify check. + maxLength: 32 + example: abcdef012345... + status: + type: string + example: "0" + requestErrorResponse: + type: object + description: Error + xml: + name: verify_response + properties: + request_id: + type: string + description: >- + The unique ID of the Verify request. This may be blank in an error situation + maxLength: 32 + example: "" + status: + type: string + example: "2" + enum: + - "0" + - "1" + - "2" + - "3" + - "4" + - "5" + - "6" + - "7" + - "8" + - "9" + - "10" + - "15" + - "16" + - "17" + - "18" + - "19" + - "20" + - "101" + description: | + Code | Text | Description + -- | -- | -- + 0 | Success | The request was successfully accepted by Nexmo. + 1 | Throttled | You are trying to send more than the maximum of 30 requests per second. + 2 | Your request is incomplete and missing the mandatory parameter `$parameter` | The stated parameter is missing. + 3 | Invalid value for parameter `$parameter` | Invalid value for parameter. If you see Facility not allowed in the error text, check that you are using the correct Base URL in your request. + 4 | Invalid credentials were provided | The supplied API key or secret in the request is either invalid or disabled. + 5 | Internal Error | An error occurred processing this request in the Cloud Communications Platform. + 6 | The Nexmo platform was unable to process this message for the following reason: `$reason` | The request could not be routed. + 7 | The number you are trying to verify is blacklisted for verification. | + 8 | The api_key you supplied is for an account that has been barred from submitting messages. | + 9 | Partner quota exceeded | Your account does not have sufficient credit to process this request. + 10 | Concurrent verifications to the same number are not allowed | + 15 | The destination number is not in a supported network | The request has been rejected. Find out more about this error in the [Knowledge Base](https://help.nexmo.com/hc/en-us/articles/360018406532-Verify-On-demand-Service-to-High-Risk-Countries) + 16 | The code inserted does not match the expected value | + 17 | The wrong code was provided too many times | You can run Verify check on a specific `request_id` up to three times unless a new verification code is generated. If you check a request more than three times, it is set to FAILED and you cannot check it again. + 18 | Too many request_ids provided | You added more than the maximum ten `request_id`s to your request. + 19 | No more events are left to execute for this request | + 20 | This account does not support the parameter: pin_code. | Only certain accounts have the ability to set the `pin_code`. Please contact your account manager for more information. + 101 | No request found | There are no matching verify requests. + error_text: + type: string + description: "If `status` is non-zero, this explains the error encountered." + example: "Your request is incomplete and missing the mandatory parameter `number`" + checkResponse: + type: object + description: Success + properties: + request_id: + type: string + description: >- + The `request_id` that you received in the response to the Verify request and + used in the Verify check request. + example: abcdef012345... + event_id: + type: string + description: The ID of the verification event, such as an SMS or TTS call. + example: 0A00000012345678 + status: + type: string + description: >- + A value of `0` indicates that your user entered the correct code. If + it is non-zero, check the `error_text`. + example: "0" + price: + type: string + description: The cost incurred for this request. + example: "0.10000000" + currency: + type: string + description: The currency code. + example: EUR + estimated_price_messages_sent: + $ref: "#/components/schemas/estimated_price_messages_sent" + xml: + name: verify_response + checkErrorResponse: + type: object + description: Error + properties: + request_id: + type: string + description: >- + The `request_id` that you received in the response to the Verify request and + used in the Verify check request. + example: abcdef012345... + status: + type: string + example: "16" + enum: + - "0" + - "1" + - "2" + - "3" + - "4" + - "5" + - "6" + - "7" + - "8" + - "9" + - "10" + - "15" + - "16" + - "17" + - "18" + - "19" + - "101" + description: | + Code | Text | Description + -- | -- | -- + 0 | Success | The request was successfully accepted by Nexmo. + 1 | Throttled | You are trying to send more than the maximum of 30 requests per second. + 2 | Your request is incomplete and missing the mandatory parameter `$parameter` | The stated parameter is missing. + 3 | Invalid value for parameter `$parameter` | Invalid value for parameter. If you see Facility not allowed in the error text, check that you are using the correct Base URL in your request. + 4 | Invalid credentials were provided | The supplied API key or secret in the request is either invalid or disabled. + 5 | Internal Error | An error occurred processing this request in the Cloud Communications Platform. + 6 | The Nexmo platform was unable to process this message for the following reason: `$reason` | The request could not be routed. + 7 | The number you are trying to verify is blacklisted for verification. | + 8 | The api_key you supplied is for an account that has been barred from submitting messages. | + 9 | Partner quota exceeded | Your account does not have sufficient credit to process this request. + 10 | Concurrent verifications to the same number are not allowed | + 15 | The destination number is not in a supported network | The request has been rejected. Find out more about this error in the [Knowledge Base](https://help.nexmo.com/hc/en-us/articles/360018406532-Verify-On-demand-Service-to-High-Risk-Countries) + 16 | The code inserted does not match the expected value | + 17 | The wrong code was provided too many times | You can run Verify check on a specific `request_id` up to three times unless a new verification code is generated. If you check a request more than three times, it is set to FAILED and you cannot check it again. + 18 | Too many request_ids provided | You added more than the maximum ten `request_id`s to your request. + 19 | No more events are left to execute for this request | + 101 | No request found | There are no matching verify requests. + error_text: + type: string + description: If the `status` is non-zero, this explains the error encountered. + example: The code inserted does not match the expected value + xml: + name: verify_response + searchResponse: + xml: + name: verify_request + type: object + description: Success + properties: + request_id: + type: string + description: >- + The `request_id` that you received in the response to the Verify request and + used in the Verify search request. + example: abcdef012345... + account_id: + type: string + description: The Nexmo account ID the request was for. + example: abcdef01 + status: + type: string + example: IN PROGRESS + enum: + - IN PROGRESS + - SUCCESS + - FAILED + - EXPIRED + - CANCELLED + description: | + Code | Description + -- | -- + IN PROGRESS | The search is still in progress. + SUCCESS | Your user entered a correct verification code. + FAILED | Your user entered an incorrect code more than three times. + EXPIRED | Your user did not enter a code before the `pin_expiry` time elapsed. + CANCELLED | The verification process was cancelled by a Verify control request. + number: + type: string + description: The phone number this verification request was used for. + example: "447700900000" + price: + type: string + description: The cost incurred for this verification request. + example: "0.10000000" + currency: + type: string + description: The currency code. + example: EUR + sender_id: + type: string + description: The `sender_id` you provided in the Verify request. + default: verify + example: mySenderId + date_submitted: + type: string + description: >- + The date and time the verification request was submitted, in the following format YYYY-MM-DD HH:MM:SS. + example: "2020-01-01 12:00:00" + date_finalized: + type: string + description: >- + The date and time the verification request was completed. This + response parameter is in the following format YYYY-MM-DD HH:MM:SS. + example: "2020-01-01 12:00:00" + first_event_date: + type: string + description: >- + The time the first verification attempt was made, in the + following format YYYY-MM-DD HH:MM:SS. + example: "2020-01-01 12:00:00" + last_event_date: + type: string + description: >- + The time the last verification attempt was made, in the + following format YYYY-MM-DD HH:MM:SS. + example: "2020-01-01 12:00:00" + checks: + type: array + xml: + wrapped: true + description: The list of checks made for this verification and their outcomes. + items: + type: object + xml: + name: check + properties: + date_received: + type: string + description: The date and time this check was received (in the format YYYY-MM-DD HH:MM:SS) + example: "2020-01-01 12:00:00" + code: + type: string + description: The code supplied with this check request + example: 987654 + status: + type: string + enum: + - VALID + - INVALID + ip_address: + type: string + description: The IP address, if available (this field is no longer used). + example: 123.0.0.255 + events: + type: array + xml: + wrapped: true + description: The events that have taken place to verify this number, and their unique identifiers. + items: + type: object + xml: + name: event + properties: + type: + type: string + enum: + - tts + - sms + id: + type: string + estimated_price_messages_sent: + $ref: "#/components/schemas/estimated_price_messages_sent" + searchErrorResponse: + xml: + name: verify_request + type: object + description: Error + properties: + request_id: + type: string + description: >- + The `request_id` that you received in the response to the Verify request and + used in the Verify search request. May be empty in an error situation. + example: abcdef012345... + status: + type: string + example: "IN PROGRESS" + enum: + - IN PROGRESS + - FAILED + - EXPIRED + - CANCELLED + - "101" + description: | + Code | Description + -- | -- + IN PROGRESS | The search is still in progress. + SUCCESS | Your user entered a correct verification code. + FAILED | Your user entered an incorrect code more than three times. + EXPIRED | Your user did not enter a code before the `pin_expiry` time elapsed. + CANCELLED | The verification process was cancelled by a Verify control request. + 101 | You supplied an invalid `request_id`, or the data is not available. Note that for recently-completed requests, there can be a delay of up to 1 minute before the results are available in search. + error_text: + type: string + description: If `status` is not `SUCCESS`, this message explains the issue encountered. + example: No response found + controlResponse: + type: object + description: Success + xml: + name: response + properties: + status: + type: string + example: "0" + description: | + `cmd` | Code | Description + -- | -- | -- + Any | 0 | Success + command: + type: string + description: The `cmd` you sent in the request. + enum: + - cancel + - trigger_next_event + example: cancel + controlErrorResponse: + type: object + description: Error + xml: + name: response + properties: + status: + type: string + example: "6" + enum: + - "1" + - "2" + - "3" + - "4" + - "5" + - "6" + - "7" + - "8" + - "9" + - "10" + - "15" + - "16" + - "17" + - "18" + - "19" + - "101" + description: | + Code | Text | Description + -- | -- | -- + 0 | Success | The request was successfully accepted by Nexmo. + 1 | Throttled | You are trying to send more than the maximum of 30 requests per second. + 2 | Your request is incomplete and missing the mandatory parameter `$parameter` | The stated parameter is missing. + 3 | Invalid value for parameter `$parameter` | Invalid value for parameter. If you see Facility not allowed in the error text, check that you are using the correct Base URL in your request. + 4 | Invalid credentials were provided | The supplied API key or secret in the request is either invalid or disabled. + 5 | Internal Error | An error occurred processing this request in the Cloud Communications Platform. + 6 | The Nexmo platform was unable to process this message for the following reason: `$reason` | The request could not be routed. + 7 | The number you are trying to verify is blacklisted for verification. | + 8 | The api_key you supplied is for an account that has been barred from submitting messages. | + 9 | Partner quota exceeded | Your account does not have sufficient credit to process this request. + 10 | Concurrent verifications to the same number are not allowed | + 15 | The destination number is not in a supported network | The request has been rejected. Find out more about this error in the [Knowledge Base](https://help.nexmo.com/hc/en-us/articles/360018406532-Verify-On-demand-Service-to-High-Risk-Countries) + 16 | The code inserted does not match the expected value | + 17 | The wrong code was provided too many times | You can run Verify check on a specific `request_id` up to three times unless a new verification code is generated. If you check a request more than three times, it is set to FAILED and you cannot check it again. + 18 | Too many request_ids provided | You added more than the maximum ten `request_id`s to your request. + 19 | For `cancel`: Either you have not waited at least 30 secs after sending a Verify request before cancelling or Verify has made too many attempts to deliver the verification code for this request and you must now wait for the process to complete. For `trigger_next_event`: All attempts to deliver the verification code for this request have completed and there are no remaining events to advance to. + 101 | No request found | There are no matching verify requests. + error_text: + type: string + description: If the `status` is non-zero, this explains the error encountered. + example: The requestId 'abcdef0123456789abcdef' does not exist or its no longer active. + securitySchemes: + apiKey: + type: apiKey + name: api_key + in: query + description: >- + You can find your API key in your Nexmo account [developer + dashboard](https://dashboard.nexmo.com/) diff --git a/tests/resources/definitions/voice.v2.yml b/tests/resources/definitions/voice.v2.yml new file mode 100755 index 00000000..c84dae13 --- /dev/null +++ b/tests/resources/definitions/voice.v2.yml @@ -0,0 +1,133 @@ +--- +openapi: 3.0.0 +info: + version: 2.0.0 + title: Voice API BETA + description: | + This is the *beta* version of the Voice API. Calls created with v2 must be managed + using [v1 endpoints](/api/voice). + + Voice v2 is provided to allow users to create IP calls. If you do not have this requirement + we recommend that you stay on v1 for now. + + > This API may break backwards compatibility at short notice (60 days) + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' +servers: +- url: https://api.nexmo.com/v2/calls +paths: + "/": + post: + security: + - bearerAuth: [] + summary: + "$ref": "voice.yml#/paths/~1/post/summary" + description: + "$ref": "voice.yml#/paths/~1/post/description" + operationId: + "$ref": "voice.yml#/paths/~1/post/operationId" + requestBody: + description: Call Details + content: + application/json: + schema: + required: + - to + - from + - event_url + properties: + to: + type: array + items: + oneOf: + - "$ref": "./voice.yml#/components/schemas/EndpointPhone" + - "$ref": "./voice.yml#/components/schemas/EndpointApp" + - "$ref": "./voice.yml#/components/schemas/EndpointSip" + - "$ref": "./voice.yml#/components/schemas/EndpointWebsocket" + - "$ref": "./voice.yml#/components/schemas/EndpointVBCExtension" + from: + "$ref": "voice.yml#/components/schemas/EndpointPhone" + ncco: + "$ref": "voice.yml#/paths/~1/post/requestBody/content/application~1json/schema/properties/ncco" + answer_url: + "$ref": "voice.yml#/paths/~1/post/requestBody/content/application~1json/schema/properties/answer_url" + answer_method: + "$ref": "voice.yml#/paths/~1/post/requestBody/content/application~1json/schema/properties/answer_method" + event_url: + "$ref": "voice.yml#/paths/~1/post/requestBody/content/application~1json/schema/properties/event_url" + event_method: + "$ref": "voice.yml#/paths/~1/post/requestBody/content/application~1json/schema/properties/event_method" + machine_detection: + "$ref": "voice.yml#/paths/~1/post/requestBody/content/application~1json/schema/properties/machine_detection" + length_timer: + "$ref": "voice.yml#/paths/~1/post/requestBody/content/application~1json/schema/properties/length_timer" + ringing_timer: + "$ref": "voice.yml#/paths/~1/post/requestBody/content/application~1json/schema/properties/ringing_timer" + responses: + '202': + description: Accepted + content: + application/json: + schema: + type: object + properties: {} + + callbacks: + delivery-receipt: + '{$request.body#/event_url}': + post: + summary: Call Placed Callback + operationId: call-placed + x-example-path: '/webhooks/event_url' + description: Each time a leg is created you'll receive a webhook containing the leg ID (`uuid`) and the conversation ID (`conversation_uuid`) in addition to the `to` and `from` addresses. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CallPlacedWebhook' + responses: + '200': + description: Your server returns this code if it accepts the callback + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + schemas: + CallPlacedWebhook: + properties: + from: + type: string + description: The source of the call + example: 14155550100 + to: + type: string + description: The destination endpoint. This can be a PSTN, SIP, IP or Websocket address + example: 14155550105 / jamie + uuid: + type: string + description: The UUID of the Conversion that the event relates to + example: 63f61863-4a51-4f6b-86e1-46edebcf9356 + conversation_uuid: + type: string + description: The UUID of the call leg that the event relates to + example: CON-f972836a-550f-45fa-956c-12a2ab5b7d22 + status: + type: string + example: started + direction: + type: string + description: Possible values are `outbound` or `inbound` + example: outbound + enum: + - outbound + - inbound + timestamp: + type: string + format: date-time + example: 2018-01-12T15:01:55.315Z diff --git a/tests/resources/definitions/voice.yml b/tests/resources/definitions/voice.yml new file mode 100755 index 00000000..d04e3712 --- /dev/null +++ b/tests/resources/definitions/voice.yml @@ -0,0 +1,883 @@ +--- +openapi: 3.0.0 +info: + version: 1.2.4 + title: Voice API + description: The Voice API lets you create outbound calls, control in-progress calls + and get information about historical calls. More information about the Voice API + can be found at . + contact: + name: Nexmo DevRel + email: devrel@nexmo.com + url: 'https://developer.nexmo.com/' +servers: +- url: https://api.nexmo.com/v1/calls +paths: + "/": + post: + security: + - bearerAuth: [] + summary: Create an outbound call + description: Create an outbound Call + operationId: createCall + requestBody: + description: Call Details + content: + application/json: + schema: + required: + - to + - from + - event_url + properties: + to: + type: array + items: + oneOf: + - "$ref": "#/components/schemas/EndpointPhone" + - "$ref": "#/components/schemas/EndpointSip" + - "$ref": "#/components/schemas/EndpointWebsocket" + - "$ref": "#/components/schemas/EndpointVBCExtension" + from: + "$ref": "#/components/schemas/EndpointPhone" + ncco: + description: | + **Required** unless `answer_url` is provided. + + The [Nexmo Call Control Object](/voice/voice-api/ncco-reference) to use for this call. + type: array + x-nexmo-developer-collection-description-shown: true + example: | + [ + { + "action":"talk", + "text": "Hello World" + } + ] + items: + type: object + answer_url: + description: | + **Required** unless `ncco` is provided. + + The webhook endpoint where you provide the [Nexmo Call Control Object](/voice/voice-api/ncco-reference) that governs this call. + type: array + x-nexmo-developer-collection-description-shown: true + example: '["https://example.com/answer"]' + items: + type: string + answer_method: + description: The HTTP method used to send event information to answer_url. + type: string + default: GET + enum: + - POST + - GET + event_url: + description: The webhook endpoint where call progress events are + sent to. For more information about the values sent, see [Event webhook](/voice/voice-api/webhook-reference#event-webhook). + type: array + x-nexmo-developer-collection-description-shown: true + example: '["https://example.com/event"]' + items: + type: string + format: uri + event_method: + description: The HTTP method used to send event information to event_url. + type: string + default: POST + enum: + - POST + - GET + machine_detection: + description: Configure the behavior when Nexmo detects that the + call is answered by voicemail. If Continue Nexmo sends an HTTP + request to event_url with the Call event machine. hangup end + the call + type: string + enum: + - continue + - hangup + example: continue + length_timer: + description: Set the number of seconds that elapse before Nexmo + hangs up after the call state changes to in_progress. + minimum: 1 + maximum: 7200 + default: 7200 + type: integer + ringing_timer: + description: Set the number of seconds that elapse before Nexmo + hangs up after the call state changes to ‘ringing’. + minimum: 1 + maximum: 120 + default: 60 + type: integer + responses: + '201': + description: Created + content: + application/json: + schema: + properties: + uuid: + $ref: '#/components/schemas/uuid' + status: + $ref: '#/components/schemas/status' + direction: + $ref: '#/components/schemas/direction' + conversation_uuid: + $ref: '#/components/schemas/conversation_uuid' + get: + security: + - bearerAuth: [] + summary: Get details of your calls + description: Get details of your calls + operationId: getCalls + parameters: + - name: status + in: query + description: Filter by call status + schema: + type: string + example: started + enum: + - started + - ringing + - answered + - machine + - completed + - busy + - cancelled + - failed + - rejected + - timeout + - unanswered + - name: date_start + in: query + description: Return the records that occurred after this point in time + schema: + type: string + format: date-time + example: 2016-11-14T07:45:14Z + - name: date_end + in: query + description: Return the records that occurred before this point in time + schema: + type: string + format: date-time + example: 2016-11-14T07:45:14Z + - name: page_size + in: query + description: Return this amount of records in the response + schema: + type: integer + maximum: 100 + minimum: 1 + default: 10 + - name: record_index + in: query + description: Return calls from this index in the response + schema: + type: integer + default: 0 + - name: order + in: query + description: Either ascending or descending order. + schema: + type: string + default: asc + enum: + - asc + - desc + - name: conversation_uuid + in: query + description: Return all the records associated with a specific conversation. + schema: + $ref: '#/components/schemas/conversation_uuid' + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + count: + type: integer + title: The total number of records returned by your request. + example: 100 + page_size: + type: integer + title: The amount of records returned in this response. + example: 10 + record_index: + type: integer + title: The `record_index` used in your request. + example: 0 + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + title: Link to the object + example: "/calls?page_size=10&record_index=20&order=asc" + _embedded: + description: >- + A list of call objects. See the + [get details of a specific call](#getCall) + response fields for a description of the nested objects + type: object + properties: + calls: + type: array + items: + type: object + properties: + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + title: The Link to the Call Object + example: "/calls/63f61863-4a51-4f6b-86e1-46edebcf9356" + uuid: + $ref: '#/components/schemas/uuid' + conversation_uuid: + $ref: '#/components/schemas/conversation_uuid' + to: + $ref: '#/components/schemas/to' + from: + $ref: '#/components/schemas/from' + status: + $ref: '#/components/schemas/status' + direction: + $ref: '#/components/schemas/direction' + rate: + $ref: '#/components/schemas/rate' + price: + $ref: '#/components/schemas/price' + duration: + $ref: '#/components/schemas/duration' + start_time: + $ref: '#/components/schemas/start_time' + end_time: + $ref: '#/components/schemas/end_time' + network: + $ref: '#/components/schemas/network' + "/{uuid}": + parameters: + - in: path + name: uuid + schema: + type: string + required: true + description: UUID of the Call + example: 63f61863-4a51-4f6b-86e1-46edebcf9356 + get: + security: + - bearerAuth: [] + summary: Get detail of a specific call + description: Get detail of a specific call + operationId: getCall + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + title: Link to the object + example: "/calls?page_size=10&record_index=20&order=asc" + uuid: + $ref: '#/components/schemas/uuid' + conversation_uuid: + $ref: '#/components/schemas/conversation_uuid' + to: + $ref: '#/components/schemas/to' + from: + $ref: '#/components/schemas/from' + status: + $ref: '#/components/schemas/status' + direction: + $ref: '#/components/schemas/direction' + rate: + $ref: '#/components/schemas/rate' + price: + $ref: '#/components/schemas/price' + duration: + $ref: '#/components/schemas/duration' + start_time: + $ref: '#/components/schemas/start_time' + end_time: + $ref: '#/components/schemas/end_time' + network: + $ref: '#/components/schemas/network' + put: + security: + - bearerAuth: [] + summary: Modify an in progress call + description: Modify an in progress call + operationId: updateCall + requestBody: + required: true + content: + application/json: + schema: + required: + - action + properties: + action: + type: string + example: mute + enum: + - hangup + - mute + - unmute + - earmuff + - unearmuff + - transfer + description: The action to apply to the in-progress call + destination: + type: object + description: Required when `action` is `transfer` + x-nexmo-developer-collection-description-shown: true + properties: + type: + type: string + example: ncco + description: Always `ncco` + x-nexmo-developer-collection-description-shown: true + url: + example: '["https://example.com/ncco.json"]' + type: array + items: + type: string + description: The URL that Nexmo makes a request to. Must return an NCCO. + x-nexmo-developer-collection-description-shown: true + ncco: + example: '[{"action": "talk", "text": "hello world"}]' + type: array + items: + type: object + description: Can be used instead of `url` for the `transfer` action only. + x-nexmo-developer-collection-description-shown: true + responses: + '204': + description: No Content + '401': + description: Unauthorized + '404': + description: Not Found + "/{uuid}/stream": + parameters: + - in: path + name: uuid + schema: + type: string + required: true + description: UUID of the Call Leg + example: 63f61863-4a51-4f6b-86e1-46edebcf9356 + put: + security: + - bearerAuth: [] + summary: Play an audio file into a call + description: Play an audio file into a call + operationId: startStream + requestBody: + description: action to perform + required: true + content: + application/json: + schema: + type: object + required: + - stream_url + properties: + stream_url: + x-nexmo-developer-collection-description-shown: true + example: '["https://example.com/waiting.mp3"]' + type: array + items: + type: string + loop: + type: integer + description: the number of times to play the file, 0 for infinite + default: 1 + level: + type: string + description: Set the audio level of the stream in the range `-1 >= level <= 1` with a precision of 0.1. The default value is 0. + example: "0.4" + default: "0" + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + message: + description: Description of the action taken + type: string + example: Stream started + uuid: + $ref: '#/components/schemas/uuid' + delete: + security: + - bearerAuth: [] + summary: Stop playing an audio file into a call + description: Stop playing an audio file into a call + operationId: stopStream + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + message: + description: Description of the action taken + type: string + example: Stream stopped + uuid: + $ref: '#/components/schemas/uuid' + "/{uuid}/talk": + parameters: + - in: path + name: uuid + schema: + type: string + required: true + description: UUID of the Call Leg + example: 63f61863-4a51-4f6b-86e1-46edebcf9356 + put: + security: + - bearerAuth: [] + summary: Play text to speech into a call + description: Play text to speech into a call + operationId: startTalk + requestBody: + description: Action to perform + content: + application/json: + schema: + required: + - text + properties: + text: + type: string + description: The text to read + example: Hello. How are you today? + voice_name: + $ref: "#/components/schemas/voice_name" + loop: + type: integer + description: The number of times to repeat the text the file, 0 + for infinite + default: 1 + level: + type: string + description: The volume level that the speech is played. This can be any value between `-1` to `1` in `0.1` increments, with `0` being the default. + example: "0.4" + default: "0" + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + message: + description: Description of the action taken + type: string + example: Talk started + uuid: + $ref: '#/components/schemas/uuid' + delete: + security: + - bearerAuth: [] + summary: Stop text to speech in a call + description: Stop text to speech in a call + operationId: stopTalk + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + message: + description: Description of the action taken + type: string + example: Talk stopped + uuid: + $ref: '#/components/schemas/uuid' + "/{uuid}/dtmf": + parameters: + - in: path + name: uuid + schema: + type: string + required: true + description: UUID of the Call Leg + example: 63f61863-4a51-4f6b-86e1-46edebcf9356 + put: + security: + - bearerAuth: [] + summary: Play DTMF tones into a call + description: Play DTMF tones into a call + operationId: startDTMF + requestBody: + description: action to perform + required: true + content: + application/json: + schema: + properties: + digits: + type: string + example: 1713 + description: The digits to send + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + message: + description: Description of the action taken + type: string + example: DTMF sent + uuid: + $ref: '#/components/schemas/uuid' +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + schemas: + network: + description: >- + The Mobile Country Code Mobile Network Code + ([MCCMNC](https://en.wikipedia.org/wiki/Mobile_country_code)) for + the carrier network used to make this call. + type: string + title: 'The Network ID of the destination ' + example: '65512' + start_time: + description: 'The time the call started in the following format: `YYYY-MM-DD HH:MM:SS`. For example, `2020-01-01 12:00:00`.' + type: string + format: timestamp + title: 'The Start Time of the call ' + example: '2020-01-01 12:00:00' + end_time: + description: 'The time the call started in the following format: `YYYY-MM-DD HH:MM:SS`. For xample, `2020-01-01 12:00:00`. This is only sent if `status` is `completed`.' + type: string + format: timestamp + title: 'The End Time of the call ' + example: '2020-01-01 12:00:00' + duration: + description: The time elapsed for the call to take place in seconds. This is only sent if `status` is `completed`. + type: string + title: 'The Duration of the call ' + example: '60' + price: + description: The total price charged for this call. This is only sent if `status` is `completed`. + type: string + title: 'The total price of the call ' + example: '23.40' + rate: + description: The price per minute for this call. This is only sent if `status` is `completed`. + type: string + title: The Price per minute of the called destination + example: '0.39' + direction: + type: string + description: Possible values are `outbound` or `inbound` + example: outbound + enum: + - outbound + - inbound + uuid: + type: string + format: uuid + title: The UUID of the Call Leg + example: 63f61863-4a51-4f6b-86e1-46edebcf9356 + description: >- + The unique identifier for this call leg. The UUID is created when + your call request is accepted by Nexmo. You use the UUID in all + requests for individual live calls + conversation_uuid: + type: string + format: uuid + title: The UUID of the Conversation + example: CON-f972836a-550f-45fa-956c-12a2ab5b7d22 + description: The unique identifier for the conversation this call leg is part of. + from: + type: array + description: The endpoint you called from. Possible values are the same as `to`. + title: The number or address that has been called + items: + type: object + properties: + type: + type: string + title: The type of Endpoint that made the call + example: phone + number: + type: string + title: The number that made the call + example: '447700900001' + to: + type: array + description: The single or mixed collection of endpoint types you connected to + title: The number or address to call + items: + type: object + properties: + type: + type: string + title: The Type of Endpoint Called + example: phone + number: + type: string + title: The number of the endpoint called + example: '447700900000' + status: + type: string + title: The State of the call + example: started + description: The status of the call. [See possible values](https://developer.nexmo.com/voice/voice-api/guides/call-flow#event-objects) + + name_user: + type: string + example: my_user_name + description: Unique name for a user + + EndpointApp: + type: object + description: Connect to an App User + required: + - type + - user + properties: + type: + type: string + example: app + description: The type of connection. Must be `app` + user: + type: string + example: jamie + description: The username to connect to + EndpointPhone: + type: object + description: Connect to a Phone (PSTN) number + required: + - type + properties: + type: + type: string + example: phone + description: The type of connection. Must be `phone` + number: + "$ref": "#/components/schemas/AddressE164" + dtmfAnswer: + description: Provide [DTMF digits](https://developer.nexmo.com/voice/voice-api/guides/dtmf) to send when the call is answered + type: string + example: p*123# + EndpointWebsocket: + type: object + description: Connect to a Websocket + required: + - type + - content-type + properties: + type: + description: The type of connection. Must be `websocket` + type: string + example: websocket + uri: + "$ref": "#/components/schemas/AddressWebsocket" + content-type: + type: string + example: audio/l16;rate=16000 + enum: + - audio/l16;rate=8000 + - audio/l16;rate=16000 + headers: + description: Details of the Websocket you want to connect to + type: object + properties: + customer_id: + type: string + example: ABC123 + description: This is an example header. You can provide any headers you may need + EndpointSip: + type: object + description: Connect to a SIP Endpoint + required: + - type + properties: + type: + description: The type of connection. Must be `sip` + type: string + example: sip + uri: + "$ref": "#/components/schemas/AddressSip" + EndpointVBCExtension: + type: object + description: Connect to a VBC extension + required: + - type + - extension + properties: + type: + type: string + example: vbc + description: The type of connection. Must be `vbc` + extension: + type: string + example: '1234' + AddressE164: + description: The phone number to connect to + type: string + minLength: 7 + maxLength: 15 + example: '14155550100' + pattern: "\\d{7,15}" + AddressSip: + description: The SIP URI to connect to + type: string + minLength: 1 + maxLength: 50 + example: sip:rebekka@sip.example.com + AddressWebsocket: + type: string + minLength: 1 + maxLength: 50 + example: wss://example.com/socket + + callEvent: + type: object + required: + - conversation_uuid + - to + - from + - uuid + - status + properties: + conversation_uuid: + description: The UUID of the Conversion that the event relates to + type: string + format: uuid + example: CON-171b3991-49b0-4dfa-badd-2bc6e68b57b4 + to: + $ref: '#/components/schemas/from' + from: + $ref: '#/components/schemas/from' + uuid: + description: The UUID of the call leg that the event relates to + type: string + format: uuid + example: 63f61863-4a51-4f6b-86e1-46edebcf9356 + timestamp: + type: string + format: date-time + example: 2018-01-12T15:01:55.315Z + status: + type: string + example: started + enum: + - started + - ringing + - answered + - machine + - completed + - timeout + - failed + - rejected + - cancelled + - busy + direction: + type: string + enum: + - inbound + - outbound + voice_name: + description: The voice & language to use + type: string + default: Kimberly + enum: + - Salli + - Joey + - Naja + - Mads + - Marlene + - Hans + - Nicole + - Russell + - Amy + - Brian + - Emma + - Geraint + - Gwyneth + - Raveena + - Chipmunk + - Eric + - Ivy + - Jennifer + - Justin + - Kendra + - Kimberly + - Conchita + - Enrique + - Penelope + - Miguel + - Chantal + - Celine + - Mathieu + - Dora + - Karl + - Carla + - Giorgio + - Liv + - Lotte + - Ruben + - Agnieszka + - Jacek + - Ewa + - Jan + - Maja + - Vitoria + - Ricardo + - Cristiano + - Ines + - Carmen + - Maxim + - Tatyana + - Astrid + - Filiz + - Mizuki + - Seoyeon diff --git a/tests/resources/definitions/vonage-business-cloud/account.yml b/tests/resources/definitions/vonage-business-cloud/account.yml new file mode 100755 index 00000000..eb580635 --- /dev/null +++ b/tests/resources/definitions/vonage-business-cloud/account.yml @@ -0,0 +1,320 @@ +--- +openapi: 3.0.0 +info: + version: 1.11.8 + title: Account API + description: | + The Vonage Business Cloud Account API enables you to retrieve information about accounts. + + Your application must subscribe to the Provisioning API suite to use this API. + contact: + name: "Vonage Business Cloud Support" + url: "https://businesssupport.vonage.com/contactus" + termsOfService: "https://www.vonage.com/business/legal-policy-center/business-cloud/tos" + +servers: + - url: "https://api.vonage.com/t/vbc.prod/provisioning" +paths: + "/api/accounts/{account_id}": + get: + operationId: AccountCtrl.getAccountServicesByAccountID + parameters: + - $ref: "#/components/parameters/AccountID" + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/AccountHalResponse" + "404": + description: Account not found + security: + - bearerAuth: [] + summary: Get account data by ID + + "/api/accounts/{account_id}/locations": + get: + operationId: AccountCtrl.getLocationsByAccountID + parameters: + - $ref: "#/components/parameters/AccountID" + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/LocationsHalResponse" + security: + - bearerAuth: [] + summary: Get account locations data by account ID + + "/api/accounts/{account_id}/locations/{location_id}": + get: + operationId: AccountCtrl.getLocationByID + parameters: + - $ref: "#/components/parameters/AccountID" + - in: path + name: location_id + required: true + schema: + type: number + description: The Vonage Business Cloud location ID + example: 327910 + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/LocationHalResponse" + "404": + description: Location not found + security: + - bearerAuth: [] + summary: Get location data by account ID and location ID + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: OAuth + + parameters: + AccountID: + in: path + name: account_id + required: true + schema: + type: number + description: The Vonage Business Cloud account ID + example: 571700 + + schemas: + FirstHref: + type: object + properties: + href: + type: string + description: URL to the first page of records + PrevHref: + type: object + properties: + href: + type: string + description: URL to the previous page of records + SelfHref: + type: object + properties: + href: + type: string + description: URL to the current page of records + NextHref: + type: object + properties: + href: + type: string + description: URL to the next page of records + LastHref: + type: object + properties: + href: + type: string + description: URL to the last page of records + Links: + type: object + properties: + first: + $ref: "#/components/schemas/FirstHref" + prev: + $ref: "#/components/schemas/PrevHref" + self: + $ref: "#/components/schemas/SelfHref" + next: + $ref: "#/components/schemas/NextHref" + last: + $ref: "#/components/schemas/LastHref" + + Address: + type: object + properties: + address_1: + type: string + example: "123 Example Street" + description: Street portion of the address + address_2: + type: string + example: "Apt. 456" + description: Additional address information + country: + type: string + example: "US" + description: Country code + state: + type: string + example: "NJ" + description: State/Province code + city: + type: string + example: "Holmdel" + description: City name + postal_code: + type: string + example: "07733" + description: Postal code + + Account: + properties: + id: + type: number + example: 571700 + description: Unique identifier of the account + name: + type: string + example: "Vonage" + description: Name of the account + status: + type: string + example: "ACTIVE" + description: Status of the account + address: + $ref: "#/components/schemas/Address" + type: object + + AccountEmbeddedObject: + type: object + properties: + data: + $ref: "#/components/schemas/Account" + description: Account object + + AddressWithTimeZone: + type: object + properties: + address_1: + type: string + example: "123 Example Street" + description: Street portion of the address + address_2: + type: string + example: "Apt. 456" + description: Additional address information + country: + type: string + example: "US" + description: Country code + state: + type: string + example: "NJ" + description: State/Province code + city: + type: string + example: "Holmdel" + description: City name + postal_code: + type: string + example: "07733" + description: Postal code + time_zone: + type: string + example: "America/New York" + description: Time zone + + Location: + type: object + properties: + id: + type: number + example: 327910 + description: Unique identifier of the location + name: + type: string + example: "Headquarters" + description: Name of the location + address: + $ref: "#/components/schemas/AddressWithTimeZone" + + LocationsEmbeddedObject: + type: object + properties: + data: + items: + $ref: "#/components/schemas/Location" + type: array + description: Collection of location objects + + LocationEmbeddedObject: + type: object + properties: + data: + $ref: "#/components/schemas/Location" + description: Location object + + AccountHalResponse: + type: object + properties: + page_size: + type: number + example: 100 + description: Number of records per page + page: + type: number + example: 1 + description: Current page number + total_pages: + type: number + example: 10 + description: Total number of pages + total_items: + type: number + example: 100 + description: Total number of records + _links: + $ref: "#/components/schemas/Links" + _embedded: + $ref: "#/components/schemas/AccountEmbeddedObject" + + LocationHalResponse: + type: object + properties: + page_size: + type: number + example: 100 + description: Number of records per page + page: + type: number + example: 1 + description: Current page number + total_pages: + type: number + example: 10 + description: Total number of pages + total_items: + type: number + example: 100 + description: Total number of records + _links: + $ref: "#/components/schemas/Links" + _embedded: + $ref: "#/components/schemas/LocationEmbeddedObject" + + LocationsHalResponse: + type: object + properties: + page_size: + type: number + example: 100 + page: + type: number + example: 1 + total_pages: + type: number + example: 10 + total_items: + type: number + example: 100 + _links: + $ref: "#/components/schemas/Links" + _embedded: + $ref: "#/components/schemas/LocationsEmbeddedObject" diff --git a/tests/resources/definitions/vonage-business-cloud/call-recording.yml b/tests/resources/definitions/vonage-business-cloud/call-recording.yml new file mode 100755 index 00000000..382b829c --- /dev/null +++ b/tests/resources/definitions/vonage-business-cloud/call-recording.yml @@ -0,0 +1,879 @@ +--- +openapi: 3.0.0 +info: + version: "1.0.1" + title: Call Recording API + description: | + The Vonage Business Cloud Call Recording API enables you to manage your company and on-demand call recordings. + + Your application must subscribe to the CallRecording API suite to use this API. + contact: + name: "Vonage Business Cloud Support" + url: "https://businesssupport.vonage.com/contactus" + termsOfService: "https://www.vonage.com/business/legal-policy-center/business-cloud/tos" +tags: + - name: Company Call Recording + description: "Company Call Recording lets administrators schedule call recording rules and recording parameters for your entire office." + - name: On-Demand Call Recording + description: "On-Demand Call Recording lets employees decide when to record calls. It’s a particularly useful phone recording system for businesses when you need to verify the details of a conversation or when it’s essential that you capture accurate notes" + - name: Export Job + description: "Export jobs let users download recordings in bulk based on search criteria. Export jobs are initiated from the corresponding company and on-demand call recording export endpoints." +paths: + "/api/accounts/{account_id}/company_call_recordings": + get: + operationId: getCCRecordings + summary: Get company call recordings + description: Get company call recordings for an account + tags: + - company call recording + security: + - bearerAuth: [] + parameters: + - $ref: "#/components/parameters/AccountID" + - $ref: "#/components/parameters/CallDirection" + - $ref: "#/components/parameters/CallID" + - $ref: "#/components/parameters/CallerID" + - $ref: "#/components/parameters/Cnam" + - $ref: "#/components/parameters/Dnis" + - $ref: "#/components/parameters/DurationGte" + - $ref: "#/components/parameters/DurationLte" + - $ref: "#/components/parameters/Extension" + - $ref: "#/components/parameters/Order" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PageSize" + - $ref: "#/components/parameters/StartGte" + - $ref: "#/components/parameters/StartLte" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/CompanyCallRecordingsHalResponse" + "401": + description: Unauthorized + "403": + description: Forbidden + "404": + description: Not Found + "/api/accounts/{account_id}/company_call_recordings/{recording_id}": + get: + operationId: getCCRecording + summary: Get single company call recording + description: Get a single company call recording for an account + tags: + - company call recording + security: + - bearerAuth: [] + parameters: + - $ref: "#/components/parameters/AccountID" + - $ref: "#/components/parameters/RecordingID" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/CompanyCallRecordingHalResponse" + "401": + description: Unauthorized + "403": + description: Forbidden + "404": + description: Not Found + delete: + operationId: deleteCCRecording + summary: Delete company call recording + description: Delete a single company call recording for an account + tags: + - company call recording + security: + - bearerAuth: [] + parameters: + - $ref: "#/components/parameters/AccountID" + - $ref: "#/components/parameters/RecordingID" + responses: + "204": + description: No Content + "401": + description: Unauthorized + "403": + description: Forbidden + "404": + description: Not Found + "/api/accounts/{account_id}/company_call_recordings/export": + post: + operationId: createCCRExportJob + summary: Create a company call recording export job + description: Create a company call recording export job for an account + tags: + - company call recording + security: + - bearerAuth: [] + parameters: + - $ref: "#/components/parameters/AccountID" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/Filters" + responses: + "202": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/CreateExportJobHalResponse" + "401": + description: Unauthorized + "403": + description: Forbidden + "404": + description: Not Found + "/api/accounts/{account_id}/users/{user_id}/call_recordings": + get: + operationId: getODCRecordings + summary: Get on-demand call recordings + description: Get on-demand call recordings for an account user + tags: + - on-demand call recording + security: + - bearerAuth: [] + parameters: + - $ref: "#/components/parameters/AccountID" + - $ref: "#/components/parameters/UserID" + - $ref: "#/components/parameters/CallDirection" + - $ref: "#/components/parameters/CallID" + - $ref: "#/components/parameters/CallerID" + - $ref: "#/components/parameters/Cnam" + - $ref: "#/components/parameters/Dnis" + - $ref: "#/components/parameters/DurationGte" + - $ref: "#/components/parameters/DurationLte" + - $ref: "#/components/parameters/Extension" + - $ref: "#/components/parameters/Order" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PageSize" + - $ref: "#/components/parameters/StartGte" + - $ref: "#/components/parameters/StartLte" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/OnDemandCallRecordingsHalResponse" + "401": + description: Unauthorized + "403": + description: Forbidden + "404": + description: Not Found + "/api/accounts/{account_id}/users/{user_id}/call_recordings/{recording_id}": + get: + operationId: getODCRecording + summary: Get single on-demand call recording + description: Get a single on-demand call recording for an account user + tags: + - on-demand call recording + security: + - bearerAuth: [] + parameters: + - $ref: "#/components/parameters/AccountID" + - $ref: "#/components/parameters/UserID" + - $ref: "#/components/parameters/RecordingID" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/OnDemandCallRecordingHalResponse" + "401": + description: Unauthorized + "403": + description: Forbidden + "404": + description: Not Found + delete: + operationId: deleteODCRecording + summary: Delete on-demand call recording + description: Delete a single on-demand call recording for an account user + tags: + - on-demand call recording + security: + - bearerAuth: [] + parameters: + - $ref: "#/components/parameters/AccountID" + - $ref: "#/components/parameters/UserID" + - $ref: "#/components/parameters/RecordingID" + responses: + "204": + description: No Content + "401": + description: Unauthorized + "403": + description: Forbidden + "404": + description: Not Found + "/api/accounts/{account_id}/users/{user_id}/call_recordings/export": + post: + operationId: createODCRExportJob + summary: Create an on-demand call recording export job + description: Create an on-demand call recording export job for an account user + tags: + - on-demand call recording + security: + - bearerAuth: [] + parameters: + - $ref: "#/components/parameters/AccountID" + - $ref: "#/components/parameters/UserID" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/Filters" + responses: + "202": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/CreateExportJobHalResponse" + "401": + description: Unauthorized + "403": + description: Forbidden + "404": + description: Not Found + "/api/accounts/{account_id}/users/{user_id}/call_recordings/jobs": + get: + operationId: getCCRExportJobs + summary: Get call recording export jobs + description: Get call recording export jobs for an account user + tags: + - export job + security: + - bearerAuth: [] + parameters: + - $ref: "#/components/parameters/AccountID" + - $ref: "#/components/parameters/UserID" + - $ref: "#/components/parameters/Status" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/ExportJobsHalResponse" + "401": + description: Unauthorized + "403": + description: Forbidden + "404": + description: Not Found + "/api/accounts/{account_id}/users/{user_id}/call_recordings/jobs/{job_id}": + get: + operationId: getCCRExportJob + summary: Get call recording export job + description: Get a single call recording export job for an account user + tags: + - export job + security: + - bearerAuth: [] + parameters: + - $ref: "#/components/parameters/AccountID" + - $ref: "#/components/parameters/UserID" + - $ref: "#/components/parameters/JobID" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/ExportJobHalResponse" + "401": + description: Unauthorized + "403": + description: Forbidden + "404": + description: Not Found +servers: + - url: https://api.vonage.com/t/vbc.prod/call_recording +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: OAuth + + parameters: + AccountID: + name: account_id + in: path + required: true + schema: + type: string + description: The Vonage Business Cloud account ID. You can use 'self' to refer to the authenticated user's account. + example: 549825 + UserID: + name: user_id + in: path + description: The Vonage Business Cloud user ID. You can use 'self' to refer to the authenticated user. + required: true + example: 745249 + schema: + type: string + JobID: + name: job_id + in: path + description: The job ID + required: true + example: 7a688e12-99ee-4816-8385-b21b90583838 + schema: + type: string + RecordingID: + name: recording_id + in: path + description: The recording ID + required: true + example: 193228 + schema: + type: string + Extension: + name: extension + in: query + description: Filter recordings by extension number + required: false + example: 999 + schema: + type: string + StartLte: + name: start:lte + in: query + description: Filter recordings by start date (less than or equal to) + example: "2019-01-01T00:00:00+0000" + required: false + schema: + type: string + StartGte: + name: start:gte + in: query + description: Filter recordings by start date (greater than or equal to) + example: "2019-01-01T00:00:00+0000" + required: false + schema: + type: string + CallDirection: + name: call_direction + in: query + description: Filter recordings by call direction + example: INBOUND + required: false + schema: + type: string + enum: + - INBOUND + - OUTBOUND + - INTRA_PBX + CallerID: + name: caller_id + in: query + description: Filter recordings by Caller ID + example: 17325550100 + required: false + schema: + type: string + CallID: + name: call_id + in: query + description: Filter recordings by Call ID + example: c91150a6-8cc4-4e73-9fef-17fd925c6448 + required: false + schema: + type: string + Cnam: + name: cnam + in: query + description: Filter recordings by CNAM (Caller ID Name) + example: 'JOHN SMITH' + required: false + schema: + type: string + Dnis: + name: dnis + in: query + description: Filter recordings by DNIS (Dialed Number Identification Service) + example: '17325550100' + required: false + schema: + type: string + DurationLte: + name: duration:lte + in: query + description: Filter recordings by duration in milliseconds (less than or equal to) + example: 60000 + required: false + schema: + type: integer + DurationGte: + name: duration:gte + in: query + description: Filter recordings by duration in milliseconds (greater than or equal to) + example: 60000 + required: false + schema: + type: integer + Status: + name: status + in: query + description: Filter export jobs by status + required: false + schema: + type: string + Order: + name: order + in: query + description: Sort recordings by field value + required: false + schema: + type: string + default: start:DESC + example: start:DESC + Page: + name: page + in: query + description: Requested page number + required: false + schema: + type: integer + default: 1 + example: 1 + PageSize: + name: page_size + in: query + description: Requested page size + required: false + schema: + type: integer + default: 10 + example: 10 + schemas: + CompanyCallRecording: + type: object + properties: + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + description: URL for the current recording + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/company_call_recordings/193228' + call_direction: + type: string + description: Direction of the recorded call + example: "INBOUND" + call_id: + type: string + description: Call ID of the recorded call + example: c91150a6-8cc4-4e73-9fef-17fd925c6448 + caller_id: + type: string + description: Caller ID of the caller of the recorded call + example: 17325550100 + cnam: + type: string + description: CNAM (Caller ID Name) of the caller of the recorded call + example: 'JOHN SMITH' + dnis: + type: string + description: DNIS (Dialed Number Identification Service) of the caller of the recorded call + example: '17325550100' + download_url: + type: string + description: URL to download the recording. This URL requires authorization with your OAuth token. + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/audio/recording/193228' + duration: + type: integer + description: Duration of the recorded call in milliseconds + example: 60000 + end: + type: string + format: date-time + description: End time of the recorded call + example: "2019-01-01T00:00:00+0000" + extensions: + type: array + items: + type: string + description: Extensions associated with the call recording + example: [999] + file_name: + type: string + description: File name of the call recording + example: "730048.11826465111463122315701.95a7584be856e34a582025d420c20d9d.1463122442148.mp3" + file_size_in_bytes: + type: integer + description: File size in bytes of the call recording + example: 100000 + id: + type: integer + description: Unique identifier of the call recording + example: 193228 + rule_ids: + type: array + description: Identifier of the call recording rule(s) that triggered the call recording + items: + type: integer + example: [1989, 2012, 449] + start: + type: string + format: date-time + description: Start time of the recorded call + example: "2019-01-01T00:00:00+0000" + CompanyCallRecordingHalResponse: + $ref: "#/components/schemas/CompanyCallRecording" + CompanyCallRecordingsHalResponse: + type: object + properties: + _embedded: + properties: + recordings: + type: array + items: + $ref: "#/components/schemas/CompanyCallRecording" + description: A list of recording objects + _links: + type: object + properties: + first: + type: object + properties: + href: + type: string + description: URL for the first page of recordings + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/company_call_recordings?page=0' + prev: + type: object + properties: + href: + type: string + description: URL for the previous page of recordings + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/company_call_recordings?page=1' + self: + type: object + properties: + href: + type: string + description: URL for the current page of recordings + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/company_call_recordings?page=2' + next: + type: object + properties: + href: + type: string + description: URL for the next page of recordings + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/company_call_recordings?page=3' + last: + type: object + properties: + href: + type: string + description: URL for the last page of recordings + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/company_call_recordings?page=4' + page: + type: integer + page_size: + type: integer + total_items: + type: integer + total_pages: + type: integer + OnDemandCallRecording: + type: object + properties: + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + description: URL for the current recording + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings/193228' + call_direction: + type: string + description: Direction of the recorded call + example: "INBOUND" + call_id: + type: string + description: Call ID of the recorded call + example: c91150a6-8cc4-4e73-9fef-17fd925c6448 + caller_id: + type: string + description: Caller ID of the caller of the recorded call + example: 17325550100 + cnam: + type: string + description: CNAM (Caller ID Name) of the caller of the recorded call + example: 'JOHN SMITH' + dnis: + type: string + description: DNIS (Dialed Number Identification Service) of the caller of the recorded call + example: '17325550100' + download_url: + type: string + description: URL to download the recording. This URL requires authorization with your OAuth token. + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/audio/recording/193228' + duration: + type: integer + description: Duration of the recorded call in milliseconds + example: 60000 + end: + type: string + format: date-time + description: End time of the recorded call + example: "2019-01-01T00:00:00+0000" + extensions: + type: array + items: + type: string + description: Extensions associated with the call recording + example: [999] + file_name: + type: string + description: File name of the call recording + example: "730048.11826465111463122315701.95a7584be856e34a582025d420c20d9d.1463122442148.mp3" + file_size_in_bytes: + type: integer + description: File size in bytes of the call recording + example: 100000 + id: + type: integer + description: Unique identifier of the call recording + example: 193228 + rule_ids: + type: array + description: Identifier of the call recording rule(s) that triggered the call recording + items: + type: integer + start: + type: string + format: date-time + description: Start time of the recorded call + example: "2019-01-01T00:00:00+0000" + OnDemandCallRecordingHalResponse: + $ref: "#/components/schemas/OnDemandCallRecording" + OnDemandCallRecordingsHalResponse: + type: object + properties: + _embedded: + properties: + recordings: + type: array + items: + $ref: "#/components/schemas/CompanyCallRecording" + description: A list of recording objects + _links: + type: object + properties: + first: + type: object + properties: + href: + type: string + description: URL for the first page of recordings + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings?page=0' + prev: + type: object + properties: + href: + type: string + description: URL for the previous page of recordings + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings?page=1' + self: + type: object + properties: + href: + type: string + description: URL for the current page of recordings + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings?page=2' + next: + type: object + properties: + href: + type: string + description: URL for the next page of recordings + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings?page=3' + last: + type: object + properties: + href: + type: string + description: URL for the last page of recordings + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings?page=4' + page: + type: integer + page_size: + type: integer + total_items: + type: integer + total_pages: + type: integer + CreateExportJobHalResponse: + type: object + properties: + job_ids: + type: array + items: + type: string + description: Unique identifier of the created export jobs + example: + - afa725fb-d418-4eaf-b3f9-0e66396fafdc + ExportJob: + type: object + properties: + _links: + type: object + properties: + self: + type: object + properties: + href: + type: string + description: URL for the current export job + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings/jobs/5e667ef2-2860-4471-8813-320980a55c20' + download_url: + type: string + description: URL to download the completed export job. This URL requires authorization with your OAuth token. + example: "https://api.vonage.com/t/vbc.prod/call_recording/api/bulkDownload/retrieve?jobId=afa725fb-d418-4eaf-b3f9-0e66396fafdc" + files_completed: + type: integer + description: Number of files completed by the export job + files_total: + type: integer + description: Number of files included in the export job + example: 10 + id: + type: string + description: Unique identifier of the export job + example: afa725fb-d418-4eaf-b3f9-0e66396fafdc + status: + type: string + description: Status of the export job + example: complete + valid_until: + type: string + format: date-time + description: Expiration time for the complete export job contents + example: "2019-01-01T00:00:00+0000" + ExportJobsHalResponse: + type: object + properties: + _embedded: + properties: + jobs: + type: array + items: + $ref: "#/components/schemas/ExportJob" + description: A list of job objects + _links: + type: object + properties: + first: + type: object + properties: + href: + type: string + description: URL for the first page of export jobs + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings/jobs?page=0' + prev: + type: object + properties: + href: + type: string + description: URL for the previous page of export jobs + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings/jobs?page=1' + self: + type: object + properties: + href: + type: string + description: URL for the current page of export jobs + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings/jobs?page=2' + next: + type: object + properties: + href: + type: string + description: URL for the next page of export jobs + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings/jobs?page=3' + last: + type: object + properties: + href: + type: string + description: URL for the last page of export jobs + example: 'https://api.vonage.com/t/vbc.prod/call_recording/api/accounts/549825/users/745249/call_recordings/jobs?page=4' + page: + type: integer + page_size: + type: integer + total_items: + type: integer + total_pages: + type: integer + ExportJobHalResponse: + $ref: "#/components/schemas/ExportJob" + Filters: + type: object + properties: + call_direction: + description: Filter recordings by call direction + example: INBOUND + type: string + enum: + - INBOUND + - OUTBOUND + - INTRA_PBX + call_id: + description: Filter recordings by Call ID + example: c91150a6-8cc4-4e73-9fef-17fd925c6448 + type: string + caller_id: + description: Filter recordings by Caller ID + type: string + example: 17325550100 + cnam: + description: Filter recordings by CNAM (Caller ID Name) + type: string + example: 'JOHN SMITH' + dnis: + description: Filter recordings by DNIS (Dialed Number Identification Service) + type: string + example: '17325550100' + duration:gte: + description: Filter recordings by duration in milliseconds (greater than or equal to) + example: 60000 + type: integer + duration:lte: + description: Filter recordings by duration in milliseconds (less than or equal to) + example: 60000 + type: integer + extension: + description: Filter recordings by extension number + example: 999 + type: string + start:gte: + description: Filter recordings by start date (greater than or equal to) + example: "2019-01-01T00:00:00+0000" + type: string + start:lte: + description: Filter recordings by start date (less than or equal to) + example: "2019-01-01T00:00:00+0000" + type: string diff --git a/tests/resources/definitions/vonage-business-cloud/extension.yml b/tests/resources/definitions/vonage-business-cloud/extension.yml new file mode 100755 index 00000000..b98d1960 --- /dev/null +++ b/tests/resources/definitions/vonage-business-cloud/extension.yml @@ -0,0 +1,321 @@ +--- +openapi: 3.0.0 +info: + version: 1.11.8 + title: Extension API + description: | + The Vonage Business Cloud Extension API enables you to retrieve information about extensions. + + Your application must subscribe to the Provisioning API suite to use this API. + contact: + name: "Vonage Business Cloud Support" + url: "https://businesssupport.vonage.com/contactus" + termsOfService: "https://www.vonage.com/business/legal-policy-center/business-cloud/tos" + +servers: + - url: "https://api.vonage.com/t/vbc.prod/provisioning" +paths: + "/api/accounts/{account_id}/extensions": + get: + operationId: ExtensionCtrl.getAccountExtensions + parameters: + - $ref: "#/components/parameters/AccountID" + - in: query + name: page_size + required: false + schema: + type: number + description: Number of records per page + example: 10 + - in: query + name: page + required: false + schema: + type: number + description: Current page number + example: 10 + - in: query + name: location_id + required: false + schema: + type: number + description: Filter by location id + example: 145214 + - in: query + name: phone_number + required: false + schema: + type: string + description: Filter by phone number + example: "14155550100" + - in: query + name: login_name + required: false + schema: + type: string + description: Filter by login name + example: "jsmith" + - in: query + name: email + required: false + schema: + type: string + description: Filter by email address + example: "john.smith@example.com" + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/EndUserRouteHalResponse" + "400": + description: Invalid parameters given + content: + application/json: + schema: + $ref: "#/components/schemas/ValidationErrorsResponse" + security: + - bearerAuth: [] + summary: Get account extensions data by account ID + + "/api/accounts/{account_id}/extensions/{extension_number}": + get: + operationId: ExtensionCtrl.getAccountExtensionByID + parameters: + - $ref: "#/components/parameters/AccountID" + - in: path + name: extension_number + required: true + schema: + type: number + description: The extension number + example: 789 + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/EndUserRouteHalResponse" + "404": + description: Extension not found + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + security: + - bearerAuth: [] + summary: Get extension data by account ID and extension number + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: OAuth + + parameters: + AccountID: + in: path + name: account_id + required: true + schema: + type: string + description: The Vonage Business Cloud account ID + + schemas: + FirstHref: + type: object + properties: + href: + type: string + description: URL to the first page of records + PrevHref: + type: object + properties: + href: + type: string + description: URL to the previous page of records + SelfHref: + type: object + properties: + href: + type: string + description: URL to the current page of records + NextHref: + type: object + properties: + href: + type: string + description: URL to the next page of records + LastHref: + type: object + properties: + href: + type: string + description: URL to the last page of records + + Links: + type: object + properties: + first: + $ref: "#/components/schemas/FirstHref" + prev: + $ref: "#/components/schemas/PrevHref" + self: + $ref: "#/components/schemas/SelfHref" + next: + $ref: "#/components/schemas/NextHref" + + BasicUser: + type: object + properties: + email: + type: string + description: Email address of the user + example: "john.smith@example.com" + login_name: + type: string + description: Login name of the user + example: "jsmith" + first_name: + type: string + description: First name of the user + example: "John" + last_name: + type: string + description: Last name of the user + example: "Smith" + + DID: + type: object + properties: + phone_number: + type: string + description: Phone number + example: "14155550100" + custom_tag: + type: string + description: Custom tag associated with the phone number + example: "My Tag" + + Line: + type: object + properties: + handset_name: + type: string + description: Name of the handset + example: "line1-VH6370927" + sip_id: + type: string + description: SIP identifier of the handset + example: "VH16370927" + + EndUserRoute: + type: object + properties: + extension_number: + type: string + description: Extension number + example: "789" + user: + $ref: "#/components/schemas/BasicUser" + location_id: + type: number + description: Unique identifier of the assigned location + example: 145214 + vtt_enabled: + type: boolean + description: Voicemail transcription status of the location + example: true + dnd_enabled: + type: boolean + description: Do Not Disturb status of the extension + example: false + caller_id: + type: string + description: Caller ID of the extension + example: "John Smith" + block_caller_id: + type: boolean + description: Block Caller ID status of the extension + example: false + dids: + items: + $ref: "#/components/schemas/DID" + type: array + description: Collection of phone numbers assigned to the extension + extension_handsets: + items: + $ref: "#/components/schemas/Line" + type: array + description: Collection of handsets assigned to the extension + + EndUserRouteEmbeddedObject: + type: object + properties: + data: + $ref: "#/components/schemas/EndUserRoute" + + EndUserRouteHalResponse: + type: object + properties: + page_size: + type: number + example: 10 + description: Number of records per page + page: + type: number + example: 1 + description: Current page number + total_pages: + type: number + example: 10 + description: Total number of pages + total_items: + type: number + example: 100 + description: Total number of records + _links: + $ref: "#/components/schemas/Links" + _embedded: + $ref: "#/components/schemas/EndUserRouteEmbeddedObject" + + ErrorResponse: + type: object + properties: + msg: + description: Error message + type: string + status: + description: Http Response Code + type: number + + ValidationErrorsResponse: + type: object + properties: + status: + description: Error status code + type: number + title: + description: Error title + type: string + instance: + description: Error Track ID + type: string + invalid_parameters: + items: + $ref: "#/components/schemas/DetailedInvalidParam" + description: Invalid parameters and their reason for failing + type: array + + DetailedInvalidParam: + type: object + properties: + name: + type: string + description: Invalid property name + reason: + type: string + description: Invalid property reason diff --git a/tests/resources/definitions/vonage-business-cloud/reports.yml b/tests/resources/definitions/vonage-business-cloud/reports.yml new file mode 100755 index 00000000..7100981f --- /dev/null +++ b/tests/resources/definitions/vonage-business-cloud/reports.yml @@ -0,0 +1,341 @@ +--- +openapi: 3.0.0 +info: + version: 1.0.0 + title: Reports API + description: | + The Vonage Business Cloud Reports API enables you to retrieve call logs for your account. + + Your application must subscribe to the Reports API suite to use this API. + contact: + name: "Vonage Business Cloud Support" + url: "https://businesssupport.vonage.com/contactus" + termsOfService: "https://www.vonage.com/business/legal-policy-center/business-cloud/tos" + +servers: + - url: "https://api.vonage.com/t/vbc.prod/reports" +paths: + "/accounts/{account_id}/call-logs": + get: + operationId: getCallLogs + summary: Retrieve call logs for your account + description: Retrieve call logs for your account + parameters: + - $ref: "#/components/parameters/AccountID" + - in: query + name: start:gte + required: true + description: "Filter records by start date (greater equal or equal to)" + schema: + type: string + example: "2019-01-01 00:00:00" + - in: query + name: start:lte + required: true + description: "Filter records by start date (less equal or equal to)" + schema: + type: string + example: "2019-01-01 00:00:00" + - in: query + name: end:gte + description: "Filter records by end date (greater equal or equal to)" + schema: + type: string + example: "2019-01-01 00:00:00" + - in: query + name: end:lte + description: "Filter records by end date (less equal or equal to)" + schema: + type: string + example: "2019-01-01 00:00:00" + - in: query + name: page_size + required: true + description: "Number of records per page" + schema: + type: number + default: 10 + example: 10 + - in: query + name: page + required: true + description: "Current page number" + schema: + type: number + default: 0 + example: 10 + - in: query + name: to + description: "Filter by called number" + schema: + type: string + example: 17325550100 + - in: query + name: from + description: "Filter by source number" + schema: + type: string + example: 17325550100 + - in: query + name: source_user + description: "Filter by source user" + schema: + type: string + - in: query + name: destination_user + description: "Filter by destination user" + schema: + type: string + - in: query + name: direction + description: "Filter by call direction." + schema: + type: string + enum: + - Inbound + - Outbound + example: Inbound + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/CallLogsHalResponse" + "400": + description: Invalid parameters given + content: + application/json: + schema: + $ref: "#/components/schemas/ValidationErrorsResponse" + security: + - bearerAuth: [] + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: OAuth + + parameters: + AccountID: + in: path + name: account_id + required: true + schema: + type: string + description: The Vonage Business Cloud account ID + example: 913874 + + schemas: + FirstHref: + type: object + properties: + href: + type: string + description: URL to the first page of records + PrevHref: + type: object + properties: + href: + type: string + description: URL to the previous page of records + SelfHref: + type: object + properties: + href: + type: string + description: URL to the current page of records + NextHref: + type: object + properties: + href: + type: string + description: URL to the next page of records + LastHref: + type: object + properties: + href: + type: string + description: URL to the last page of records + + Links: + type: object + properties: + first: + $ref: "#/components/schemas/FirstHref" + prev: + $ref: "#/components/schemas/PrevHref" + self: + $ref: "#/components/schemas/SelfHref" + next: + $ref: "#/components/schemas/NextHref" + + CallLogsHalResponse: + type: object + properties: + page_size: + type: number + example: 10 + description: Number of records per page + page: + type: number + example: 1 + description: Current page number + total_pages: + type: number + example: 10 + description: Total number of pages + total_items: + type: number + example: 100 + description: Total number of records + _links: + $ref: "#/components/schemas/Links" + _embedded: + $ref: "#/components/schemas/CallLogsEmbeddedObject" + + CallLogsEmbeddedObject: + type: object + properties: + call_logs: + items: + $ref: "#/components/schemas/CallLog" + type: array + + CallLog: + type: object + properties: + id: + type: string + description: Unique identifier of the call + example: f27b937d-6dde-441a-9595-006e7302eac1 + in_network: + type: boolean + description: Indicates if call was on/off network + example: true + international: + type: boolean + description: Indicates if call was international + example: false + from: + type: string + description: Source number of the call + example: 17325550100 + to: + type: string + description: Destination number of the call + example: 17325550100 + direction: + type: string + description: Direction of the call + example: "Inbound" + length: + type: number + description: Duration of the call in seconds + example: 60 + start: + type: string + description: Start time of the call + example: "2019-01-01 00:00:00" + end: + type: string + description: End time of the call + example: "2019-01-01 00:00:00" + charge: + type: number + description: Amount charged for the call + example: 0 + rate: + type: number + description: Rate charged for the call + example: 0 + destination_device_name: + type: string + description: Name of the destination device of the call + example: "Smith" + destination_user_full_name: + type: string + description: Full name of the destination user of the call + example: "John Smith" + destination_user: + type: string + description: Destination user of the call + example: "JSmith" + destination_sip_id: + type: string + description: SIP ID of the destination device of the call + example: "VH1111111" + destination_extension: + type: number + description: Destination extension of the call + example: 1000 + + source_device_name: + type: string + description: Name of the source device of the call + example: "Smith" + source_user_full_name: + type: string + description: Full name of the source user of the call + example: "John Smith" + source_user: + type: string + description: Source user of the call + example: "JSmith" + source_sip_id: + type: string + description: SIP ID of the source device of the call + example: "VH1111111" + source_extension: + type: number + description: Source extension of the call + example: 1000 + + result: + type: string + description: Result of the call + example: Answered + recorded: + type: boolean + description: Indicates if call was recorded + example: true + + ErrorResponse: + type: object + properties: + msg: + description: Error message + type: string + status: + description: Http Response Code + type: number + + ValidationErrorsResponse: + type: object + properties: + status: + description: Error status code + type: number + title: + description: Error title + type: string + instance: + description: Error Track ID + type: string + invalid_parameters: + items: + $ref: "#/components/schemas/DetailedInvalidParam" + description: Invalid parameters and their reason for failing + type: array + + DetailedInvalidParam: + type: object + properties: + name: + type: string + description: Invalid property name + reason: + type: string + description: Invalid property reason diff --git a/tests/resources/definitions/vonage-business-cloud/user.yml b/tests/resources/definitions/vonage-business-cloud/user.yml new file mode 100755 index 00000000..9d2e8a69 --- /dev/null +++ b/tests/resources/definitions/vonage-business-cloud/user.yml @@ -0,0 +1,339 @@ +--- +openapi: 3.0.0 +info: + version: 1.11.8 + title: User API + description: | + The Vonage Business Cloud User API enables you to retrieve information about users. + + Your application must subscribe to the Provisioning API suite to use this API. + contact: + name: "Vonage Business Cloud Support" + url: "https://businesssupport.vonage.com/contactus" + termsOfService: "https://www.vonage.com/business/legal-policy-center/business-cloud/tos" + +servers: + - url: "https://api.vonage.com/t/vbc.prod/provisioning" +paths: + "/api/accounts/{account_id}/users": + get: + operationId: UserCtrl.getUsers + parameters: + - $ref: "#/components/parameters/AccountID" + - in: query + name: page_size + required: false + schema: + type: number + description: Number of records per page + example: 10 + - in: query + name: page + required: false + schema: + type: number + description: Current page number + example: 10 + - in: query + name: first_name + required: false + schema: + type: string + description: Filter by first name + example: "John" + - in: query + name: last_name + required: false + schema: + type: string + description: Filter by last name + example: "Smith" + - in: query + name: login_name + required: false + schema: + type: string + description: Filter by login name + example: "jsmith" + - in: query + name: email + required: false + schema: + type: string + description: Filter by email address + example: "john.smith@example.com" + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/UsersHalResponse" + "400": + description: Invalid parameters given + content: + application/json: + schema: + $ref: "#/components/schemas/ValidationErrorsResponse" + security: + - bearerAuth: [] + summary: Get account users data by account ID + + "/api/accounts/{account_id}/users/{user_id}": + get: + operationId: UserCtrl.getUserByID + parameters: + - $ref: "#/components/parameters/AccountID" + - in: path + name: user_id + required: true + schema: + type: number + description: The Vonage Business Cloud user ID + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/UserHalResponse" + "404": + description: User not found + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + security: + - bearerAuth: [] + summary: Get user data by account ID and user ID +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: OAuth + + parameters: + AccountID: + in: path + name: account_id + required: true + schema: + type: string + description: The Vonage Business Cloud account ID + example: 451496 + + schemas: + FirstHref: + type: object + properties: + href: + type: string + description: URL to the first page of records + PrevHref: + type: object + properties: + href: + type: string + description: URL to the previous page of records + SelfHref: + type: object + properties: + href: + type: string + description: URL to the current page of records + NextHref: + type: object + properties: + href: + type: string + description: URL to the next page of records + LastHref: + type: object + properties: + href: + type: string + description: URL to the last page of records + + Links: + type: object + properties: + first: + $ref: "#/components/schemas/FirstHref" + prev: + $ref: "#/components/schemas/PrevHref" + self: + $ref: "#/components/schemas/SelfHref" + next: + $ref: "#/components/schemas/NextHref" + + Contact: + type: object + properties: + type: + type: string + description: Contact type + example: "Home" + value: + type: string + description: Contact value + example: "14155550100" + + UserExtension: + type: object + properties: + dids: + items: + $ref: "#/components/schemas/DID" + type: array + description: Collection of phone numbers assigned to the extension + extension_number: + type: string + description: Extension number + example: "789" + + User: + type: object + properties: + email: + type: string + description: Email address of the user + example: "john.smith@example.com" + login_name: + type: string + description: Login name of the user + example: "jsmith" + first_name: + type: string + description: First name of the user + example: "John" + last_name: + type: string + description: Last name of the user + example: "Smith" + id: + type: number + description: Unique identifier of the user + example: 869048 + contact_numbers: + items: + $ref: "#/components/schemas/Contact" + type: array + description: Collection of contact objects + extensions: + items: + $ref: "#/components/schemas/UserExtension" + type: array + description: Collection of extension objects + + UsersEmbeddedObject: + type: object + properties: + data: + items: + $ref: "#/components/schemas/User" + type: array + + UserEmbeddedObject: + type: object + properties: + data: + $ref: "#/components/schemas/User" + + UserHalResponse: + type: object + properties: + page_size: + type: number + example: 10 + description: Number of records per page + page: + type: number + example: 1 + description: Current page number + total_pages: + type: number + example: 10 + description: Total number of pages + total_items: + type: number + example: 100 + description: Total number of records + _links: + $ref: "#/components/schemas/Links" + _embedded: + $ref: "#/components/schemas/UserEmbeddedObject" + + UsersHalResponse: + type: object + properties: + page_size: + type: number + example: 10 + description: Number of records per page + page: + type: number + example: 1 + description: Current page number + total_pages: + type: number + example: 10 + description: Total number of pages + total_items: + type: number + example: 100 + description: Total number of records + _links: + $ref: "#/components/schemas/Links" + _embedded: + $ref: "#/components/schemas/UsersEmbeddedObject" + + DID: + type: object + properties: + phone_number: + type: string + description: Phone number + example: "14155550100" + custom_tag: + type: string + description: Custom tag associated with the phone number + example: My Tag + + ErrorResponse: + type: object + properties: + msg: + description: Error message + type: string + status: + description: Http Response Code + type: number + + ValidationErrorsResponse: + type: object + properties: + status: + description: Error status code + type: number + title: + description: Error title + type: string + instance: + description: Error Track ID + type: string + invalid_parameters: + items: + $ref: "#/components/schemas/DetailedInvalidParam" + description: Invalid parameters and their reason for failing + type: array + + DetailedInvalidParam: + type: object + properties: + name: + type: string + description: Invalid property name + reason: + type: string + description: Invalid property reason diff --git a/tests/resources/definitions/vonage-business-cloud/vgis.yml b/tests/resources/definitions/vonage-business-cloud/vgis.yml new file mode 100755 index 00000000..e6fa8b28 --- /dev/null +++ b/tests/resources/definitions/vonage-business-cloud/vgis.yml @@ -0,0 +1,1556 @@ +openapi: 3.0.0 +info: + description: | + The Vonage Integration Suite API enables call control and webhooks for call events. + + Your application must subscribe to the VonageIntegrationSuite API suite to use this API. + version: 1.0.1 + title: Vonage Integration Suite + contact: + email: gunifydevops@vonage.com + name: VGIS + url: https://integrate.vonage.com +tags: + - name: Users + - name: Accounts + - name: Calls + - name: Events + - name: Webhooks + description: + Webhooks are external URLs which subscribe to receive events via HTTP POST for a + specified set of events. +servers: + - url: "https://api.vonage.com/t/vbc.prod/vis/v1" +paths: + /self: + get: + summary: User info + operationId: getUser + tags: + - Users + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/User" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + /self/account: + get: + summary: Account info + operationId: getAccount + tags: + - Accounts + responses: + "200": + description: Successful + content: + application/json: + schema: + $ref: "#/components/schemas/Account" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + /self/calls: + post: + tags: + - Calls + summary: Place a call + operationId: createCall + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/CallCreate" + description: Place call parameters + required: true + responses: + "201": + description: Successful + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Call" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + get: + tags: + - Calls + summary: List active calls + description: Lists currently active calls + operationId: listCalls + parameters: + - in: query + name: fromDate + description: Return calls that occurred after this point in time + schema: + type: integer + #format: date + - in: query + name: toDate + description: Return calls that occurred before this point in time + schema: + type: integer + #format: date + - in: query + name: direction + description: Filter by call direction. For multiple criteria, seperate values by a comma. + schema: + type: string + example: INBOUND,OUTBOUND + enum: + - INBOUND + - OUTBOUND + - in: query + name: states + description: Filter calls by state. For multiple criteria, seperate values by a comma. + schema: + type: string + default: ACTIVE + example: ACTIVE,RINGING + enum: + - INITIALIZING + - RINGING + - ACTIVE + - HELD + - REMOTE_HELD + - in: query + name: offset + description: "Page number of calls to return" + schema: + type: integer + format: int64 + - in: query + name: size + description: Return this amount of calls in the response + schema: + type: integer + default: 20 + - in: query + name: order + description: Sort in either ascending or descending order + schema: + type: string + default: ASC + enum: + - DESC + - ASC + - in: query + name: sort + description: "Sort calls by property" + schema: + type: string + responses: + "200": + description: Successful + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Call" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + /self/calls/count: + get: + tags: + - Calls + summary: Get calls count + operationId: getCallsCount + parameters: + - in: query + name: fromDate + description: Return calls that occurred after this point in time + schema: + type: integer + #format: date + - in: query + name: toDate + description: Return calls that occurred before this point in time + schema: + type: integer + #format: date + - in: query + name: direction + description: Filter by call direction. For multiple criteria, seperate values by a comma. + schema: + type: string + example: INBOUND,OUTBOUND + enum: + - INBOUND + - OUTBOUND + - in: query + name: states + description: Filter calls by state. For multiple criteria, seperate values by a comma. + schema: + type: string + default: ACTIVE + example: ACTIVE,RINGING + enum: + - INITIALIZING + - RINGING + - ACTIVE + - HELD + - REMOTE_HELD + responses: + "200": + description: Successful + content: + application/json: + schema: + $ref: "#/components/schemas/EventsCount" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + "/self/calls/{id}": + get: + tags: + - Calls + summary: Get a call + operationId: getRoles + parameters: + - name: id + in: path + description: Unique identifier of the call + required: true + schema: + type: string + responses: + "200": + description: Successful + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Call" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + delete: + tags: + - Calls + summary: End a call + operationId: destroyCall + parameters: + - name: id + in: path + description: Unique identifier of the call + required: true + schema: + type: string + responses: + "200": + description: Successful + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Call" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + "/self/calls/{id}/answer": + put: + tags: + - Calls + summary: Answer call (On supported devices) + operationId: callAnswer + parameters: + - name: id + in: path + description: Unique identifier of the call + required: true + schema: + type: string + responses: + "200": + description: Successful + content: + application/json: + schema: + $ref: "#/components/schemas/Call" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + "/self/calls/{id}/hold": + put: + tags: + - Calls + summary: Put call on hold + operationId: callHold + parameters: + - name: id + in: path + description: Unique identifier of the call + required: true + schema: + type: string + responses: + "200": + description: Successful + content: + application/json: + schema: + $ref: "#/components/schemas/Call" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + delete: + tags: + - Calls + summary: Unhold + operationId: callUnold + parameters: + - name: id + in: path + description: Unique identifier of the call + required: true + schema: + type: string + responses: + "200": + description: Successful + content: + application/json: + schema: + $ref: "#/components/schemas/Call" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + "/self/calls/{id}/vmtransfer": + put: + tags: + - Calls + summary: Send call to voicemail + operationId: callVMTransfer + parameters: + - name: id + in: path + description: Unique identifier of the call + required: true + schema: + type: string + responses: + "200": + description: Successful + content: + application/json: + schema: + $ref: "#/components/schemas/Call" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + "/self/calls/{id}/transfer": + post: + tags: + - Calls + summary: Transfer call + operationId: callTransfer + parameters: + - name: id + in: path + description: Unique identifier of the call + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/CallTransfer" + description: Call transfer parameters + required: true + responses: + "200": + description: Successful + content: + application/json: + schema: + $ref: "#/components/schemas/Call" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + /self/events: + get: + tags: + - Events + summary: List events + operationId: listEvents + parameters: + - in: query + name: types + description: Record type + schema: + type: string + example: CALL + enum: + - CALL + - in: query + name: fromDate + description: Return events that occurred after this point in time + schema: + type: integer + #format: date + - in: query + name: toDate + description: Return events that occurred before this point in time + schema: + type: integer + #format: date + - in: query + name: direction + description: Filter by event direction + schema: + type: string + example: INBOUND,OUTBOUND + enum: + - INBOUND + - OUTBOUND + - in: query + name: states + description: Filter events by state + schema: + type: string + example: ACTIVE,RINGING + enum: + - INITIALIZING + - RINGING + - ACTIVE + - HELD + - REMOTE_HELD + - DETACHED + - REJECTED + - CANCELLED + - ANSWERED + - MISSED + - in: query + name: offset + description: Page number of events to return + schema: + type: integer + format: int64 + - in: query + name: size + description: Return this amount of events in the response + schema: + type: integer + default: 20 + - in: query + name: order + description: Sort in either ascending or descending order' + schema: + type: string + default: ASC + enum: + - DESC + - ASC + - in: query + name: sort + description: "Sort events by property" + schema: + type: string + responses: + "200": + description: Successful + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Event" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + /self/events/count: + get: + tags: + - Events + summary: Get events count + operationId: getEventsCount + parameters: + - in: query + name: fromDate + description: Return events that occurred after this point in time + schema: + type: integer + #format: date + - in: query + name: toDate + description: Return events that occurred before this point in time + schema: + type: integer + #format: date + - in: query + name: direction + description: Filter by event direction + schema: + type: string + example: INBOUND,OUTBOUND + enum: + - INBOUND + - OUTBOUND + - in: query + name: states + description: Filter events by state + schema: + type: string + example: ACTIVE,RINGING + enum: + - INITIALIZING + - RINGING + - ACTIVE + - HELD + - REMOTE_HELD + responses: + "200": + description: Successful + content: + application/json: + schema: + $ref: "#/components/schemas/EventsCount" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + "/self/events/{id}": + get: + tags: + - Events + summary: Get event + operationId: getEvent + parameters: + - name: id + in: path + description: Unique identifier of the event + required: true + schema: + type: string + responses: + "200": + description: Successful + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Event" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + /self/webhooks: + post: + tags: + - Webhooks + summary: Create a new webhook subscription + operationId: createWebhook + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/WebhookCreate" + description: Webhook create parameters + required: true + responses: + "201": + description: Successful + content: + application/json: + schema: + $ref: "#/components/schemas/Webhook" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + get: + tags: + - Webhooks + summary: List web hooks + operationId: listWebhooks + responses: + "200": + description: Successful + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Webhook" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + "/self/webhooks/{id}": + get: + tags: + - Webhooks + summary: Get web hook details + operationId: viewWebhook + parameters: + - name: id + in: path + description: Unique identifier of the webhook + required: true + schema: + type: string + responses: + "200": + description: Successful + content: + application/json: + schema: + $ref: "#/components/schemas/Webhook" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + delete: + tags: + - Webhooks + summary: Remove a web hook + operationId: destroyWebhook + parameters: + - name: id + in: path + description: Unique identifier of the webhook + required: true + schema: + type: string + responses: + "204": + description: Successful + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + "/self/webhooks/{id}/renew": + put: + tags: + - Webhooks + summary: Renews a web hook + operationId: renewWebhook + parameters: + - name: id + in: path + description: Webhook ID + required: true + schema: + type: string + responses: + "200": + description: Successful + content: + application/json: + schema: + $ref: "#/components/schemas/Webhook" + "400": + description: + "Bad Request: The client should not repeat the request without + modifications" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: "Unauthorized: Invalid access token" + "403": + description: "Forbidden: The user has no rights to access the resource(s)" + "408": + description: "Timeout: The client may repeat the request without modifications" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal Server Error + "502": + description: Bad Gateway + x-auth-type: Application & Application User + x-throttling-tier: Unlimited +components: + schemas: + Error: + type: object + properties: + field: + type: string + message: + type: string + ErrorResponse: + type: object + properties: + errorCode: + type: string + errorMessage: + type: string + errors: + type: array + items: + $ref: "#/components/schemas/Error" + Event: + type: object + required: + - id + - type + - accountId + - userId + - uciId + - direction + - state + - phoneNumber + - startTime + properties: + id: + type: integer + format: int64 + description: Unique identifier of the event + externalId: + type: string + description: External identifier of the event + type: + type: string + enum: + - CALL + description: Record type + accountId: + type: integer + format: int64 + description: Unique identifier of the account + userId: + type: integer + format: int64 + description: Unique identifier of the user + uciId: + type: integer + format: int64 + description: Unique identifier of communications provider + direction: + type: string + enum: + - INBOUND + - OUTBOUND + description: Direction of the event + callerId: + type: string + description: Remote caller ID + phoneNumber: + type: string + description: Unique identifier of the account + duration: + type: integer + format: int64 + description: Duration of the call in milliseconds + smsData: + type: string + state: + type: string + enum: + - INITIALIZING + - RINGING + - ACTIVE + - HELD + - REMOTE_HELD + - DETACHED + - REJECTED + - CANCELLED + - ANSWERED + - MISSED + description: Status of the event + startTime: + type: string + format: date + description: Start time of the event + answerTime: + type: string + format: date + description: Time to answer the event + endTime: + type: string + format: date + description: End time of the event + EventsCount: + type: object + properties: + count: + type: integer + format: int64 + description: Number of events found + Call: + type: object + required: + - id + - type + - accountId + - userId + - uciId + - direction + - duration + - state + - phoneNumber + - startTime + properties: + id: + type: integer + format: int64 + description: Unique identifier of the call + externalId: + type: string + description: External identifier of the call + type: + type: string + enum: + - CALL + description: Record type + accountId: + type: integer + format: int64 + description: Unique identifier of the account + userId: + type: integer + format: int64 + description: Unique identifier of the user + uciId: + type: integer + format: int64 + description: Unique identifier of communications provider + direction: + type: string + enum: + - INBOUND + - OUTBOUND + description: Direction of the call + callerId: + type: string + description: Remote caller ID + phoneNumber: + type: string + description: Unique identifier of the account + duration: + type: integer + format: int64 + description: Duration of the call in milliseconds + state: + type: string + enum: + - INITIALIZING + - RINGING + - ACTIVE + - HELD + - REMOTE_HELD + - DETACHED + - REJECTED + - CANCELLED + - ANSWERED + - MISSED + description: Status of the call + startTime: + type: string + format: date + description: Start time of the call + answerTime: + type: string + format: date + description: Time to answer the call + endTime: + type: string + format: date + description: End time of the call + CallCreate: + type: object + required: + - phoneNumber + properties: + phoneNumber: + type: string + description: Phone number to call + CallTransfer: + type: object + required: + - phoneNumber + properties: + phoneNumber: + type: string + description: Phone number to transfer to + WebhookCreate: + type: object + properties: + url: + type: string + example: https://www.example.com + description: Destination URL for events + events: + type: array + example: + - CALL + description: Events to subscribe to the webhook + items: + type: string + example: CALL + enum: + - CALL + signingAlgo: + type: string + example: HMAC_SHA256 + description: Signing algorithm for the webhook + enum: + - HMAC_SHA256 + signingKey: + type: string + description: Signing key for the webhook + metadataPolicy: + type: string + example: NONE + description: Metadata policy for the webhook + enum: + - NONE + - HEADER + - BODY + Webhook: + type: object + properties: + id: + type: string + example: 184094 + description: Unique identifier for the webhook + userId: + type: string + example: 522078 + description: Unique identifier of the user + accountId: + type: string + example: 257073 + description: Unique identifier of the account + url: + type: string + example: https://www.example.com + description: Destination URL for events + status: + type: string + example: ACTIVE + description: Status for the webhook + enum: + - ACTIVE + - PAUSED + events: + type: array + example: + - CALL + description: Subscribed events for the webhook + items: + type: string + enum: + - CALL + signingAlgo: + type: string + example: HMAC_SHA256 + description: Signing algorithm for the webhook + enum: + - HMAC_SHA256 + - NONE + metadataPolicy: + type: string + example: NONE + description: Metadata policy for the webhook + enum: + - NONE + - HEADER + - BODY + expireAt: + type: string + example: 2019-01-01T00:00:00.000Z + description: Expiration time for the webhook + createdAt: + type: string + example: 2019-01-01T00:00:00.000Z + description: Created time for the webhook + renewedAt: + type: string + example: 2019-01-01T00:00:00.000Z + description: Last renewed time for the webhook + purgeAt: + type: string + example: 2019-01-01T00:00:00.000Z + description: Scheduled purge time for the webhook + signingKey: + type: string + description: Signing key for the webhook + statistics: + type: object + properties: + totalAttempts: + type: integer + example: 10 + description: Total delivery attempts + totalSuccesses: + type: integer + example: 10 + description: Total successful deliveries + totalFailures: + type: integer + example: 10 + description: Total failed deliveries + failed: + type: boolean + example: false + description: Current delivery status + User: + type: object + properties: + id: + type: integer + example: 522078 + description: Unique identifier of the user + accountId: + type: integer + example: 257073 + description: Unique identifier of the user's account + acountLabel: + type: string + example: Vonage + description: The name of the user's account + firstName: + type: string + example: Robert + description: First name of the user + lastName: + type: string + example: Smith + description: Last name of the user + emailAddress: + type: string + example: john.smith@example.com + description: Email address of the user + contactNumber: + type: string + example: 14155550100 + description: Contact number of the user + status: + type: string + example: ACTIVE + description: Status of the user + enum: + - PENDING + - ACTIVE + - DELETED + - ARCHIVED + ucis: + type: array + items: + type: object + properties: + id: + type: integer + format: int64 + ucpLabel: + type: string + health: + type: object + properties: + status: + type: string + message: + type: string + type: + type: string + default: USER_UCI + roles: + type: array + items: + type: object + properties: + code: + type: string + example: AU + description: Code for the role + name: + type: string + example: Account User + description: Name for the role + Account: + type: object + properties: + id: + type: integer + example: 257073 + description: Unique identifier of the account + name: + type: string + example: Vonage + description: Name of the account + org: + type: string + example: Vonage + description: Organization of the account + ucis: + type: array + items: + type: object + properties: + id: + type: integer + format: int64 + ucpLabel: + type: string + ucpAccountId: + type: string + health: + type: object + properties: + status: + type: string + message: + type: string + type: + type: string + default: ACCOUNT_UCI + status: + type: string + example: ACTIVE + description: Status of the account + enum: + - PENDING + - ACTIVE + - DELETED + - ARCHIVED diff --git a/tests/spec/MediaTypeTest.php b/tests/spec/MediaTypeTest.php index 125e721d..65b3e755 100644 --- a/tests/spec/MediaTypeTest.php +++ b/tests/spec/MediaTypeTest.php @@ -103,7 +103,7 @@ public function testCreateionFromObjects() $this->assertInstanceOf(\cebe\openapi\spec\Encoding::class, $mediaType->encoding['profileImage']); } - public function badEncodingProvider() + public static function badEncodingProvider() { yield [['encoding' => ['id' => 'foo']], 'Encoding MUST be either array or Encoding object, "string" given']; yield [['encoding' => ['id' => 42]], 'Encoding MUST be either array or Encoding object, "integer" given']; diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php old mode 100644 new mode 100755 index 20b568ed..8c8856a0 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -1,6 +1,7 @@ assertEquals([ 'OpenApi is missing required property: openapi', 'OpenApi is missing required property: info', - 'OpenApi is missing required property: paths', + 'OpenApi is missing at least one of the following required properties: paths, webhooks, components', ], $openapi->getErrors()); // check default value of servers @@ -28,7 +29,7 @@ public function testEmpty() public function testReadPetStore() { - $openApiFile = __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/petstore.yaml'; + $openApiFile = __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/petstore.yaml'; $yaml = Yaml::parse(file_get_contents($openApiFile)); $openapi = new OpenApi($yaml); @@ -88,58 +89,62 @@ public function assertAllInstanceOf($className, $array) } } - public function specProvider() + public static function specProvider() { // examples from https://github.com/OAI/OpenAPI-Specification/tree/master/examples/v3.0 $oaiExamples = [ // TODO symfony/yaml can not read this file!? -// __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/api-with-examples.yaml', - __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/callback-example.yaml', - __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/link-example.yaml', - __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/petstore.yaml', - __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/petstore-expanded.yaml', - __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/uspto.yaml', +// __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/api-with-examples.yaml', + __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/callback-example.yaml', + __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/link-example.yaml', + __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/petstore.yaml', + __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/petstore-expanded.yaml', + __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/uspto.yaml', ]; // examples from https://github.com/Mermade/openapi3-examples $mermadeExamples = [ - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/externalPathItemRef.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/deprecated.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/swagger2openapi/openapi.json', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Different_parameters.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Fixed_file.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Different_parameters.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Fixed_file.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Fixed_multipart.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Improved_examples.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Improved_pathdescriptions.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Improved_securityschemes.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Improved_serverseverywhere.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._New_callbacks.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._New_links.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._Different_parameters.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._Different_requestbody.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._Different_servers.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._Fixed_multipart.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._Improved_securityschemes.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._New_callbacks.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._New_links.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example3_from_._Different_parameters.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example3_from_._Different_servers.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example4_from_._Different_parameters.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example5_from_._Different_parameters.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/externalPathItemRef.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/deprecated.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/swagger2openapi/openapi.json', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Different_parameters.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Fixed_file.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Fixed_multipart.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Improved_examples.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Improved_pathdescriptions.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Improved_securityschemes.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Improved_serverseverywhere.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._New_callbacks.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._New_links.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._Different_parameters.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._Different_requestbody.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._Different_servers.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._Fixed_multipart.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._Improved_securityschemes.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._New_callbacks.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._New_links.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example3_from_._Different_parameters.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example3_from_._Different_servers.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example4_from_._Different_parameters.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example5_from_._Different_parameters.md.yaml', // TODO symfony/yaml can not read this file!? -// __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/OAI/api-with-examples.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/OAI/petstore-expanded.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/OAI/petstore.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/OAI/uber.yaml', - - __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/rapid7-html.json', - __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/rapid7-java.json', - __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/rapid7-js.json', - __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/rapid7-php.json', - __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/rapid7-ruby.json', -// __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/yamlbomb.yaml', +// __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/OAI/api-with-examples.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/OAI/petstore-expanded.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/OAI/petstore.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/OAI/uber.yaml', + + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/rapid7-html.json', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/rapid7-java.json', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/rapid7-js.json', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/rapid7-php.json', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/rapid7-ruby.json', +// __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/yamlbomb.yaml', + + // OpenAPI 3.1 examples + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.1/pass/minimal_comp.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.1/pass/minimal_hooks.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.1/pass/minimal_paths.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.1/pass/path_var_empty_pathitem.yaml', ]; // examples from https://github.com/APIs-guru/openapi-directory/tree/openapi3.0.0/APIs @@ -154,10 +159,9 @@ public function specProvider() $it->next(); } - // examples from https://github.com/Nexmo/api-specification/tree/master/definitions $nexmoExamples = []; /** @var $it RecursiveDirectoryIterator|RecursiveIteratorIterator */ - $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__ . '/../../vendor/nexmo/api-specification/definitions')); + $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__ . '/../resources/definitions')); $it->rewind(); while($it->valid()) { if ($it->getExtension() === 'yml' @@ -176,9 +180,8 @@ public function specProvider() $nexmoExamples ); foreach($all as $path) { - yield [ - substr($path, strlen(__DIR__ . '/../../vendor/')), - basename(dirname($path, 2)) . DIRECTORY_SEPARATOR . basename(dirname($path, 1)) . DIRECTORY_SEPARATOR . basename($path) + yield $path => [ + $path ]; } } @@ -189,10 +192,10 @@ public function specProvider() public function testSpecs($openApiFile) { if (strtolower(substr($openApiFile, -5, 5)) === '.json') { - $json = json_decode(file_get_contents(__DIR__ . '/../../vendor/' . $openApiFile), true); + $json = json_decode(file_get_contents($openApiFile), true); $openapi = new OpenApi($json); } else { - $yaml = Yaml::parse(file_get_contents(__DIR__ . '/../../vendor/' . $openApiFile)); + $yaml = Yaml::parse(file_get_contents($openApiFile)); $openapi = new OpenApi($yaml); } $openapi->setDocumentContext($openapi, new \cebe\openapi\json\JsonPointer('')); @@ -202,7 +205,7 @@ public function testSpecs($openApiFile) $this->assertTrue($result); // openapi - $this->assertStringStartsWith('3.0.', $openapi->openapi); + $this->assertNotSame(OpenApi::VERSION_UNSUPPORTED, $openapi->getMajorVersion()); // info $this->assertInstanceOf(\cebe\openapi\spec\Info::class, $openapi->info); @@ -211,10 +214,15 @@ public function testSpecs($openApiFile) $this->assertAllInstanceOf(\cebe\openapi\spec\Server::class, $openapi->servers); // paths - if ($openapi->components !== null) { + if ($openapi->paths !== null) { $this->assertInstanceOf(\cebe\openapi\spec\Paths::class, $openapi->paths); } + // webhooks + if ($openapi->webhooks !== null) { + $this->assertAllInstanceOf(\cebe\openapi\spec\PathItem::class, $openapi->webhooks); + } + // components if ($openapi->components !== null) { $this->assertInstanceOf(\cebe\openapi\spec\Components::class, $openapi->components); @@ -232,4 +240,31 @@ public function testSpecs($openApiFile) } } + + public function testVersions() + { + $yaml = <<assertTrue($openapi->validate(), print_r($openapi->getErrors(), true)); + $this->assertEquals('3.0', $openapi->getMajorVersion()); + + $yaml = <<assertTrue($openapi->validate(), print_r($openapi->getErrors(), true)); + $this->assertEquals('3.1', $openapi->getMajorVersion()); + + + } } diff --git a/tests/spec/PathTest.php b/tests/spec/PathTest.php index 6c46e4b0..b901aa71 100644 --- a/tests/spec/PathTest.php +++ b/tests/spec/PathTest.php @@ -66,7 +66,7 @@ public function testRead() } } - public function testCreateionFromObjects() + public function testCreationFromObjects() { $paths = new Paths([ '/pets' => new PathItem([ @@ -88,7 +88,7 @@ public function testCreateionFromObjects() $this->assertSame('The pets list is gone 🙀', $paths->getPath('/pets')->get->responses->getResponse(404)->description); } - public function badPathsConfigProvider() + public static function badPathsConfigProvider() { yield [['/pets' => 'foo'], 'Path MUST be either array or PathItem object, "string" given']; yield [['/pets' => 42], 'Path MUST be either array or PathItem object, "integer" given']; diff --git a/tests/spec/ReferenceTest.php b/tests/spec/ReferenceTest.php index b94c946e..6ee6c939 100644 --- a/tests/spec/ReferenceTest.php +++ b/tests/spec/ReferenceTest.php @@ -657,4 +657,49 @@ public function testResolveRelativePathAll() } } + public function testReferenceExtraFields() + { + /** @var $openapi OpenApi */ + $openapi = Reader::readFromYaml(<<<'YAML' +openapi: 3.1.0 +info: + title: test api + version: 1.0.0 +components: + schemas: + Pet: + type: object + properties: + id: + type: integer +paths: + '/pet': + get: + responses: + 200: + description: return a pet + content: + 'application/json': + schema: + $ref: "#/components/schemas/Pet" + summary: 'Pet Schema' + description: 'This is a pet schema' +YAML + , OpenApi::class); + + $result = $openapi->validate(); + $this->assertEquals([], $openapi->getErrors()); + $this->assertTrue($result); + + /** @var $petResponse Response */ + $petResponse = $openapi->paths->getPath('/pet')->get->responses['200']; + $this->assertInstanceOf(Reference::class, $ref = $petResponse->content['application/json']->schema); + $this->assertEquals('Pet Schema', $ref->getSummary()); + $this->assertEquals('This is a pet schema', $ref->getDescription()); + + $openapi->resolveReferences(new \cebe\openapi\ReferenceContext($openapi, 'file:///tmp/openapi.yaml')); + + $this->assertInstanceOf(Schema::class, $refSchema = $petResponse->content['application/json']->schema); + $this->assertSame($openapi->components->schemas['Pet'], $refSchema); + } } diff --git a/tests/spec/ResponseTest.php b/tests/spec/ResponseTest.php index d11b8e27..be240bcd 100644 --- a/tests/spec/ResponseTest.php +++ b/tests/spec/ResponseTest.php @@ -174,7 +174,7 @@ public function testCreateionFromObjects() $this->assertSame('The pets list is gone 🙀', $responses->getResponse(404)->description); } - public function badResponseProvider() + public static function badResponseProvider() { yield [['200' => 'foo'], 'Response MUST be either an array, a Response or a Reference object, "string" given']; yield [['200' => 42], 'Response MUST be either an array, a Response or a Reference object, "integer" given']; diff --git a/tests/spec/SchemaTest.php b/tests/spec/SchemaTest.php index 1600b3b0..27016949 100644 --- a/tests/spec/SchemaTest.php +++ b/tests/spec/SchemaTest.php @@ -102,6 +102,20 @@ public function testMinMax() $this->assertTrue($schema->exclusiveMaximum); $this->assertNull($schema->minimum); $this->assertNull($schema->exclusiveMinimum); + + /** @var $schema Schema */ + $schema = Reader::readFromJson('{"type": "integer", "exclusiveMaximum": 10}', Schema::class); + $this->assertNull($schema->maximum); + $this->assertSame(10, $schema->exclusiveMaximum); + $this->assertNull($schema->minimum); + $this->assertNull($schema->exclusiveMinimum); + + /** @var $schema Schema */ + $schema = Reader::readFromJson('{"type": "integer", "exclusiveMinimum": 10}', Schema::class); + $this->assertNull($schema->maximum); + $this->assertNull($schema->exclusiveMaximum); + $this->assertNull($schema->minimum); + $this->assertSame(10, $schema->exclusiveMinimum); } public function testReadObject() @@ -225,7 +239,7 @@ public function testCreateionFromObjects() } - public function badSchemaProvider() + public static function badSchemaProvider() { yield [['properties' => ['a' => 'foo']], 'Unable to instantiate cebe\openapi\spec\Schema Object with data \'foo\'']; yield [['properties' => ['a' => 42]], 'Unable to instantiate cebe\openapi\spec\Schema Object with data \'42\'']; @@ -420,4 +434,31 @@ public function testPropertyNameRef() $this->assertEquals('string', $person->properties['name']->type); $this->assertEquals('string', $person->properties['$ref']->type); } + + public function testArrayKeyIsPerseveredInPropertiesThatAreArrays() + { + $json = <<<'JSON' +{ + "webhooks": { + "branch-protection-rule-created": { + "post": { + "description": "A branch protection rule was created.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + } +} +JSON; + $openApi = Reader::readFromJson($json); + self::assertArrayHasKey('branch-protection-rule-created', $openApi->webhooks); + } }